diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj
index d0157e405..f6284c697 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj
+++ b/Builds/VisualStudio2015/RippleD.vcxproj
@@ -1325,6 +1325,12 @@
+
+ True
+ True
+
+
+
@@ -1375,12 +1381,6 @@
-
- True
- True
-
-
-
True
True
@@ -1821,36 +1821,26 @@
-
-
-
-
True
True
-
+
True
True
+
+
True
True
-
- True
- True
-
+
+
-
-
-
-
-
-
@@ -4187,6 +4177,10 @@
True
True
+
+ True
+ True
+
True
True
@@ -4255,10 +4249,6 @@
True
True
-
- True
- True
-
True
True
@@ -4399,22 +4389,10 @@
True
True
-
- True
- True
-
-
- True
- True
-
True
True
-
- True
- True
-
True
True
diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters
index 9cd64989a..6a688f3b9 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters
@@ -1839,6 +1839,12 @@
ripple\app\tx\impl
+
+ ripple\app\tx\impl
+
+
+ ripple\app\tx\impl
+
ripple\app\tx\impl
@@ -1890,12 +1896,6 @@
ripple\app\tx\impl
-
- ripple\app\tx\impl
-
-
- ripple\app\tx\impl
-
ripple\app\tx\impl
@@ -2439,39 +2439,27 @@
ripple\conditions
-
- ripple\conditions
-
ripple\conditions
-
- ripple\conditions\impl
-
ripple\conditions\impl
-
+
ripple\conditions\impl
+
+ ripple\conditions\impl
+
ripple\conditions\impl
-
+
ripple\conditions\impl
-
+
ripple\conditions\impl
-
- ripple\conditions
-
-
- ripple\conditions
-
-
- ripple\conditions
-
ripple\core
@@ -4983,6 +4971,9 @@
test\app
+
+ test\app
+
test\app
@@ -5034,9 +5025,6 @@
test\app
-
- test\app
-
test\app
@@ -5142,18 +5130,9 @@
test\beast
-
- test\conditions
-
-
- test\conditions
-
test\conditions
-
- test\conditions
-
test\core
diff --git a/src/ripple/app/main/Amendments.cpp b/src/ripple/app/main/Amendments.cpp
index 1399a2de5..893bae3dc 100644
--- a/src/ripple/app/main/Amendments.cpp
+++ b/src/ripple/app/main/Amendments.cpp
@@ -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" }
};
}
diff --git a/src/ripple/app/tx/impl/SusPay.cpp b/src/ripple/app/tx/impl/Escrow.cpp
similarity index 73%
rename from src/ripple/app/tx/impl/SusPay.cpp
rename to src/ripple/app/tx/impl/Escrow.cpp
index d000a10a8..18bc1cc85 100644
--- a/src/ripple/app/tx/impl/SusPay.cpp
+++ b/src/ripple/app/tx/impl/Escrow.cpp
@@ -18,7 +18,7 @@
//==============================================================================
#include
-#include
+#include
#include
#include
#include
@@ -32,7 +32,7 @@
#include
#include
-// 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(
- 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(cb) != static_cast(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;
diff --git a/src/ripple/app/tx/impl/SusPay.h b/src/ripple/app/tx/impl/Escrow.h
similarity index 89%
rename from src/ripple/app/tx/impl/SusPay.h
rename to src/ripple/app/tx/impl/Escrow.h
index aa52fae7f..bc42c2c60 100644
--- a/src/ripple/app/tx/impl/SusPay.h
+++ b/src/ripple/app/tx/impl/Escrow.h
@@ -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
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)
{
}
diff --git a/src/ripple/app/tx/impl/applySteps.cpp b/src/ripple/app/tx/impl/applySteps.cpp
index 9a1e98d49..1c0f1ac0e 100644
--- a/src/ripple/app/tx/impl/applySteps.cpp
+++ b/src/ripple/app/tx/impl/applySteps.cpp
@@ -25,12 +25,12 @@
#include
#include
#include
+#include
#include
#include
#include
#include
#include
-#include
#include
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(ctx);
case ttOFFER_CREATE: return invoke_preclaim(ctx);
case ttPAYMENT: return invoke_preclaim(ctx);
- case ttSUSPAY_CREATE: return invoke_preclaim(ctx);
- case ttSUSPAY_FINISH: return invoke_preclaim(ctx);
- case ttSUSPAY_CANCEL: return invoke_preclaim(ctx);
+ case ttESCROW_CREATE: return invoke_preclaim(ctx);
+ case ttESCROW_FINISH: return invoke_preclaim(ctx);
+ case ttESCROW_CANCEL: return invoke_preclaim(ctx);
case ttREGULAR_KEY_SET: return invoke_preclaim(ctx);
case ttSIGNER_LIST_SET: return invoke_preclaim(ctx);
case ttTICKET_CANCEL: return invoke_preclaim(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(tx);
case ttOFFER_CREATE: return invoke_calculateConsequences(tx);
case ttPAYMENT: return invoke_calculateConsequences(tx);
- case ttSUSPAY_CREATE: return invoke_calculateConsequences(tx);
- case ttSUSPAY_FINISH: return invoke_calculateConsequences(tx);
- case ttSUSPAY_CANCEL: return invoke_calculateConsequences(tx);
+ case ttESCROW_CREATE: return invoke_calculateConsequences(tx);
+ case ttESCROW_FINISH: return invoke_calculateConsequences(tx);
+ case ttESCROW_CANCEL: return invoke_calculateConsequences(tx);
case ttREGULAR_KEY_SET: return invoke_calculateConsequences(tx);
case ttSIGNER_LIST_SET: return invoke_calculateConsequences(tx);
case ttTICKET_CANCEL: return invoke_calculateConsequences(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(); }
@@ -341,4 +341,4 @@ doApply(PreclaimResult const& preclaimResult,
}
}
-} // ripple
\ No newline at end of file
+} // ripple
diff --git a/src/ripple/conditions/Condition.h b/src/ripple/conditions/Condition.h
index 422611aff..0c92a5ea9 100644
--- a/src/ripple/conditions/Condition.h
+++ b/src/ripple/conditions/Condition.h
@@ -20,73 +20,91 @@
#ifndef RIPPLE_CONDITIONS_CONDITION_H
#define RIPPLE_CONDITIONS_CONDITION_H
-#include // use Beast implementation
+#include
+#include
#include
-#include
#include
#include
+#include
#include
+#include
+#include
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
+ 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 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 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
-loadCondition(std::string const& s);
-
-boost::optional
-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
-to_blob (Condition const& c);
-
}
}
diff --git a/src/ripple/conditions/Ed25519.h b/src/ripple/conditions/Ed25519.h
deleted file mode 100644
index d126b425d..000000000
--- a/src/ripple/conditions/Ed25519.h
+++ /dev/null
@@ -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
-#include
-#include
-#include
-#include
-#include
-
-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 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
diff --git a/src/ripple/conditions/Fulfillment.h b/src/ripple/conditions/Fulfillment.h
index e1657c5ae..c02964193 100644
--- a/src/ripple/conditions/Fulfillment.h
+++ b/src/ripple/conditions/Fulfillment.h
@@ -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
+ deserialize(
+ Slice s,
+ std::error_code& ec);
+
public:
virtual ~Fulfillment() = default;
- Fulfillment () = default;
-
- /** Returns the size of the fulfillment's payload. */
- virtual
- std::size_t
- payloadSize() const = 0;
-
- /** Returns the fulfillment's payload */
+ /** Returns the fulfillment's fingerprint:
+
+ 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
-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
-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
-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);
diff --git a/src/ripple/conditions/PrefixSha256.h b/src/ripple/conditions/PrefixSha256.h
deleted file mode 100644
index 643e2c600..000000000
--- a/src/ripple/conditions/PrefixSha256.h
+++ /dev/null
@@ -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
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace ripple {
-namespace cryptoconditions {
-
-class PrefixSha256 final
- : public Fulfillment
-{
- Buffer prefix_;
- std::unique_ptr 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(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(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 subfulfillment)
- {
- subfulfillment_ = std::move (subfulfillment);
- }
-};
-
-}
-
-}
-
-#endif
diff --git a/src/ripple/conditions/PreimageSha256.h b/src/ripple/conditions/PreimageSha256.h
deleted file mode 100644
index 24cd10d80..000000000
--- a/src/ripple/conditions/PreimageSha256.h
+++ /dev/null
@@ -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
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-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(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
diff --git a/src/ripple/conditions/impl/Condition.cpp b/src/ripple/conditions/impl/Condition.cpp
index 72bbee835..961150794 100644
--- a/src/ripple/conditions/impl/Condition.cpp
+++ b/src/ripple/conditions/impl/Condition.cpp
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -29,160 +30,212 @@
namespace ripple {
namespace cryptoconditions {
-boost::optional
-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
+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 (match[1]);
-
- if (!isCondition (c.type))
- return boost::none;
-
- c.featureBitmask = parse_hexadecimal(match[2]);
- c.maxFulfillmentLength = parse_decimal(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
-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 (
- start, finish);
+ if (ec)
+ return {};
- if (!isCondition (c.type))
- return boost::none;
+ p = parsePreamble(s, ec);
- std::tie (start, c.featureBitmask) =
- oer::decode_varuint (
- 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(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 (
- 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(type, cost, std::move(b));
+}
+
+}
+
+std::unique_ptr
+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
-to_blob (Condition const& c)
-{
- // TODO: optimize this
- std::vector 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 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;
}
}
-
}
diff --git a/src/ripple/conditions/impl/Ed25519.cpp b/src/ripple/conditions/impl/Ed25519.cpp
deleted file mode 100644
index 59d6dc9fb..000000000
--- a/src/ripple/conditions/impl/Ed25519.cpp
+++ /dev/null
@@ -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
-#include
-#include
-#include
-#include
-#include
-
-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;
-}
-
-}
-
-}
diff --git a/src/ripple/conditions/impl/Fulfillment.cpp b/src/ripple/conditions/impl/Fulfillment.cpp
index fe69ee360..cbb658573 100644
--- a/src/ripple/conditions/impl/Fulfillment.cpp
+++ b/src/ripple/conditions/impl/Fulfillment.cpp
@@ -19,10 +19,7 @@
#include
#include
-#include
-#include
-#include
-#include
+#include
#include
#include
#include
@@ -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
-loadFulfillment (std::uint16_t type, Slice payload)
+Fulfillment::deserialize(
+ Slice s,
+ std::error_code& ec)
{
- std::unique_ptr 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();
+ 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 f;
+
+ switch (static_cast(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();
+ case Type::prefixSha256:
+ ec = error::unsupported_type;
+ return {};
break;
- case condition_rsa_sha256:
- p = std::make_unique();
+ case Type::thresholdSha256:
+ ec = error::unsupported_type;
+ return {};
break;
- case condition_ed25519:
- p = std::make_unique();
+ 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
-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(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
-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 (
- 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
-to_blob (Fulfillment const& f)
-{
- // NIKB TODO optimize this
- std::vector 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;
}
}
diff --git a/src/ripple/conditions/impl/PreimageSha256.h b/src/ripple/conditions/impl/PreimageSha256.h
new file mode 100644
index 000000000..704ad5a48
--- /dev/null
+++ b/src/ripple/conditions/impl/PreimageSha256.h
@@ -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
+#include
+#include
+#include
+#include
+#include
+#include
+
+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
+ 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(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(h);
+ return{ d.data(), d.size() };
+ }
+
+ std::uint32_t
+ cost() const override
+ {
+ return static_cast(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
diff --git a/src/ripple/conditions/impl/RsaSha256.cpp b/src/ripple/conditions/impl/RsaSha256.cpp
deleted file mode 100644
index a4884c595..000000000
--- a/src/ripple/conditions/impl/RsaSha256.cpp
+++ /dev/null
@@ -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
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-namespace ripple {
-namespace cryptoconditions {
-
-namespace detail {
-
-struct rsa_deleter
-{
- void operator() (RSA* rsa) const
- {
- RSA_free (rsa);
- }
-};
-
-using RsaKey = std::unique_ptr;
-
-struct bn_deleter
-{
- void operator() (BIGNUM* bn) const
- {
- BN_free (bn);
- }
-};
-
-using BigNum = std::unique_ptr;
-
-// 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(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(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 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(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(static_cast(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;
-}
-
-}
-
-}
diff --git a/src/ripple/conditions/impl/base64.h b/src/ripple/conditions/impl/base64.h
deleted file mode 100644
index 8e8edc0ed..000000000
--- a/src/ripple/conditions/impl/base64.h
+++ /dev/null
@@ -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
-#include
-#include
-#include
-#include
-#include
-#include
-
-/*
- Portions from http://www.adp-gmbh.ch/cpp/common/base64.html
- Copyright notice:
-
- base64.cpp and base64.h
-
- Copyright (C) 2004-2008 René 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é 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
-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
-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
-std::string
-base64url_encode (std::array const& d)
-{
- return base64url_encode (d.data(), d.size());
-}
-
-template
-std::string
-base64url_encode (std::vector const& d)
-{
- return base64url_encode (d.data(), d.size());
-}
-
-template
-std::string
-base64url_encode (Buffer const& d)
-{
- return base64url_encode (d.data(), d.size());
-}
-
-template
-std::string
-base64url_encode (Slice d)
-{
- return base64url_encode (d.data(), d.size());
-}
-
-template
-std::vector
-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 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(
- 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(
- 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
diff --git a/src/ripple/conditions/impl/error.cpp b/src/ripple/conditions/impl/error.cpp
new file mode 100644
index 000000000..668c52244
--- /dev/null
+++ b/src/ripple/conditions/impl/error.cpp
@@ -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
+#include
+#include
+#include
+
+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(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::type>(ev),
+ detail::get_cryptoconditions_error_category()
+ };
+}
+
+}
+}
diff --git a/src/ripple/conditions/RsaSha256.h b/src/ripple/conditions/impl/error.h
similarity index 52%
rename from src/ripple/conditions/RsaSha256.h
rename to src/ripple/conditions/impl/error.h
index 1ffb48ef3..e30c14a77 100644
--- a/src/ripple/conditions/RsaSha256.h
+++ b/src/ripple/conditions/impl/error.h
@@ -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
-#include
-#include
-#include
+#include
+#include
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
+{
+ static bool const value = true;
+};
+
+} // std
#endif
diff --git a/src/ripple/conditions/impl/utils.h b/src/ripple/conditions/impl/utils.h
index b59ad312b..20b976ffa 100644
--- a/src/ripple/conditions/impl/utils.h
+++ b/src/ripple/conditions/impl/utils.h
@@ -21,6 +21,9 @@
#define RIPPLE_CONDITIONS_UTILS_H
#include
+#include
+#include
+#include
#include
#include
#include
@@ -31,338 +34,208 @@
namespace ripple {
namespace cryptoconditions {
-inline
-std::string
-hexstr (std::vector 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
-hexblob (std::string const& s)
+bool
+isConstructed(Preamble const& p)
{
- std::vector result;
- result.reserve (1 + (s.size () / 2));
-
- auto iter = s.cbegin ();
-
- if (s.size () & 1)
- {
- int c = charUnHex (*iter++);
-
- if (c < 0)
- Throw("Invalid hex in blob");
-
- result.push_back(c);
- }
-
- while (iter != s.cend ())
- {
- int cHigh = charUnHex (*iter++);
-
- if (cHigh < 0)
- Throw("Invalid hex in blob");
-
- int cLow = charUnHex (*iter);
-
- if (cLow < 0)
- Throw("Invalid hex in blob");
-
- iter++;
-
- result.push_back (
- static_cast(cHigh << 4) |
- static_cast(cLow));
- }
-
- return result;
+ return !isPrimitive(p);
}
-template
-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
-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
-std::string
-to_hex (Integer value)
-{
- std::stringstream ss;
- ss << std::hex << value;
- return ss.str();
-}
-
-template
-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
-void
-encode_integer(Integer value, OutputIt out)
-{
- static_assert (
- std::is_same::value ||
- std::is_same::value ||
- std::is_same::value ||
- std::is_same::value,
- "encode_integer accepts only std::uint{8,16,32,64}_t");
-
- std::size_t n = sizeof(Integer);
-
- while(n--)
- {
- *out++ = static_cast(
- (value >> (n * 8)) & 0xFF);
- }
-}
-
-// Simple conversion: big-endian byte stream to integer
-template
-std::pair
-decode_integer(InputIt begin, InputIt end)
-{
- static_assert (
- std::is_same::value ||
- std::is_same::value ||
- std::is_same::value ||
- std::is_same::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("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
inline
-OutputIt
-encode_length (std::size_t len, OutputIt out)
+bool
+isUniversal(Preamble const& p)
{
- if (len <= 0x7F)
- {
- *out++ = static_cast(len & 0x7F);
- return out;
- }
-
- // Decide how many bytes we need:
- if (len <= 0xFFFF)
- {
- *out++ = 0x82;
- *out++ = static_cast((len >> 8) & 0xFF);
- *out++ = static_cast(len & 0xFF);
- return out;
- }
-
- if (len <= 0xFFFFFF)
- {
- *out++ = 0x83;
- *out++ = static_cast((len >> 16) & 0xFF);
- *out++ = static_cast