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((len >> 8) & 0xFF); - *out++ = static_cast(len & 0xFF); - return out; - } - - if (len <= 0xFFFFFFFF) - { - *out++ = 0x84; - *out++ = static_cast((len >> 24) & 0xFF); - *out++ = static_cast((len >> 16) & 0xFF); - *out++ = static_cast((len >> 8) & 0xFF); - *out++ = static_cast(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("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 -std::pair -decode_length (InputIt begin, InputIt end) -{ - if (begin == end) - Throw("empty buffer"); - - std::size_t bytes = *begin++; - - if (bytes < 128) - return { begin, bytes }; - - bytes &= 0x7F; - - if (bytes > 4) - Throw("overlong encoded length: " + std::to_string(bytes)); - - std::size_t len = 0; - - if (std::distance (begin, end) < bytes) - Throw("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 -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("overlong encoding length: " + std::to_string(size)); + return (p.type & 0xC0) == 0x40; } -/** Encode an dynamic size octet string: OER 2.6 (1) */ -template -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 -std::enable_if_t::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( - (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 -std::enable_if_t::value, std::pair> -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("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 +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::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 diff --git a/src/ripple/ledger/impl/ApplyStateTable.cpp b/src/ripple/ledger/impl/ApplyStateTable.cpp index ee0b46f86..973b1f420 100644 --- a/src/ripple/ledger/impl/ApplyStateTable.cpp +++ b/src/ripple/ledger/impl/ApplyStateTable.cpp @@ -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); diff --git a/src/ripple/protocol/Feature.h b/src/ripple/protocol/Feature.h index 69d2d0d52..107933b70 100644 --- a/src/ripple/protocol/Feature.h +++ b/src/ripple/protocol/Feature.h @@ -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 diff --git a/src/ripple/protocol/Indexes.h b/src/ripple/protocol/Indexes.h index b82d9850f..86504537d 100644 --- a/src/ripple/protocol/Indexes.h +++ b/src/ripple/protocol/Indexes.h @@ -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 diff --git a/src/ripple/protocol/LedgerFormats.h b/src/ripple/protocol/LedgerFormats.h index 856c90ad1..d7bb895ea 100644 --- a/src/ripple/protocol/LedgerFormats.h +++ b/src/ripple/protocol/LedgerFormats.h @@ -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', diff --git a/src/ripple/protocol/TxFormats.h b/src/ripple/protocol/TxFormats.h index 36b23c049..4a3bd1473 100644 --- a/src/ripple/protocol/TxFormats.h +++ b/src/ripple/protocol/TxFormats.h @@ -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, diff --git a/src/ripple/protocol/impl/Feature.cpp b/src/ripple/protocol/impl/Feature.cpp index 8430c28ba..87ab127c6 100644 --- a/src/ripple/protocol/impl/Feature.cpp +++ b/src/ripple/protocol/impl/Feature.cpp @@ -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 diff --git a/src/ripple/protocol/impl/Indexes.cpp b/src/ripple/protocol/impl/Indexes.cpp index 169d6e578..c328395c1 100644 --- a/src/ripple/protocol/impl/Indexes.cpp +++ b/src/ripple/protocol/impl/Indexes.cpp @@ -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(h) }; + return { ltESCROW, static_cast(h) }; } Keylet diff --git a/src/ripple/protocol/impl/LedgerFormats.cpp b/src/ripple/protocol/impl/LedgerFormats.cpp index 49e15bbab..978555d55 100644 --- a/src/ripple/protocol/impl/LedgerFormats.cpp +++ b/src/ripple/protocol/impl/LedgerFormats.cpp @@ -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) << diff --git a/src/ripple/protocol/impl/TxFormats.cpp b/src/ripple/protocol/impl/TxFormats.cpp index 6a0bf18b4..10bbfdf54 100644 --- a/src/ripple/protocol/impl/TxFormats.cpp +++ b/src/ripple/protocol/impl/TxFormats.cpp @@ -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); diff --git a/src/ripple/unity/app_tx.cpp b/src/ripple/unity/app_tx.cpp index e84af3d91..d1a669efa 100644 --- a/src/ripple/unity/app_tx.cpp +++ b/src/ripple/unity/app_tx.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -35,7 +36,6 @@ #include #include #include -#include #include #include #include diff --git a/src/ripple/unity/conditions.cpp b/src/ripple/unity/conditions.cpp index 6abc7d4c2..54c53bfca 100644 --- a/src/ripple/unity/conditions.cpp +++ b/src/ripple/unity/conditions.cpp @@ -21,5 +21,4 @@ #include #include -#include -#include +#include diff --git a/src/test/app/SusPay_test.cpp b/src/test/app/Escrow_test.cpp similarity index 80% rename from src/test/app/SusPay_test.cpp rename to src/test/app/Escrow_test.cpp index fd8055fa1..2693baba6 100644 --- a/src/test/app/SusPay_test.cpp +++ b/src/test/app/Escrow_test.cpp @@ -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 const fb1 = + // A PreimageSha256 fulfillments and its associated condition. + std::array 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 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 const fb2 = + // Another PreimageSha256 fulfillments and its associated condition. + std::array 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 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 const fb3 = + // Another PreimageSha256 fulfillment and its associated condition. + std::array const fb3 = {{ - 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, + 0xA0, 0x06, 0x80, 0x04, 0x6E, 0x69, 0x6B, 0x62 }}; std::array 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 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 diff --git a/src/test/conditions/Ed25519_test.cpp b/src/test/conditions/Ed25519_test.cpp deleted file mode 100644 index b314b89ad..000000000 --- a/src/test/conditions/Ed25519_test.cpp +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include - -namespace ripple { -namespace cryptoconditions { - -class Ed25519_test : public beast::unit_test::suite -{ - void - check ( - std::array const& secretKey, - std::vector 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 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 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 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 const v1 (512, 0x21); - std::vector 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); - -} - -} diff --git a/src/test/conditions/PrefixSha256_test.cpp b/src/test/conditions/PrefixSha256_test.cpp deleted file mode 100644 index 6bad71c85..000000000 --- a/src/test/conditions/PrefixSha256_test.cpp +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include -#include - -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 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 ( - makeSlice (prefix1))); - - PrefixSha256 f2; - f2.setPrefix(makeSlice (prefix2)); - f2.setSubfulfillment(std::make_unique ( - 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 ( - makeSlice (prefix1))); - - PrefixSha256 f2; - f2.setPrefix(makeSlice (prefix2)); - f2.setSubfulfillment(std::make_unique ( - 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 ( - SecretKey{ sk }, makeSlice (prefix1))); - - PrefixSha256 f2; - f2.setPrefix(makeSlice (prefix2)); - f2.setSubfulfillment(std::make_unique ( - 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 ( - 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 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 ( - SecretKey{ sk }, makeSlice (abcdef)); - - // Inner - auto pif = std::make_unique(); - pif->setPrefix(makeSlice (abc)); - pif->setSubfulfillment (std::move(edf)); - - // Outer - auto pof = std::make_unique(); - 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 ( - makeSlice (v)); - - // Inner - auto pif = std::make_unique(); - pif->setPrefix(makeSlice (abc)); - pif->setSubfulfillment (std::move(img)); - - // Outer - auto pof = std::make_unique(); - 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); - -} - -} diff --git a/src/test/conditions/PreimageSha256_test.cpp b/src/test/conditions/PreimageSha256_test.cpp index b6ce15383..5595d553d 100644 --- a/src/test/conditions/PreimageSha256_test.cpp +++ b/src/test/conditions/PreimageSha256_test.cpp @@ -17,13 +17,16 @@ */ //============================================================================== +#include #include +#include #include #include #include -#include +#include #include -#include +#include +#include #include namespace ripple { @@ -31,305 +34,155 @@ namespace cryptoconditions { class PreimageSha256_test : public beast::unit_test::suite { - void - check ( - std::vector 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 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(cHigh << 4) | + static_cast(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 const v1 (256, 0x00); - check (v1, - "cf:0:" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "AAAAAAAAAAAAAA", - "cc:0:3:U0HmsmRpeacOV2UwB6HzEBaUIeyb3Z8aVkj3Wt4AWvE:256"); - - std::vector const v2 (256, 0xFF); - check (v2, - "cf:0:" - "__________________________________________________________________________________" - "__________________________________________________________________________________" - "__________________________________________________________________________________" - "__________________________________________________________________________________" - "_____________w", - "cc:0:3:PWh2oBRt6FdusjlahY3hIT0bksZbd53zozHP1aRYRUY:256"); - - std::vector 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 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 v; - - // As long as we don't exceed the fulfillment length - // we should succeed: - unexcept ([this, &v]() + std::pair 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 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 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(); } }; diff --git a/src/test/conditions/RsaSha256_test.cpp b/src/test/conditions/RsaSha256_test.cpp deleted file mode 100644 index 4a05e779a..000000000 --- a/src/test/conditions/RsaSha256_test.cpp +++ /dev/null @@ -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 -#include -#include -#include -#include -#include - -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 aaa {{ 'a', 'a', 'a' }}; - std::array 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); - -} - -} diff --git a/src/test/unity/app_test_unity.cpp b/src/test/unity/app_test_unity.cpp index bb6bde5ba..8b12846ff 100644 --- a/src/test/unity/app_test_unity.cpp +++ b/src/test/unity/app_test_unity.cpp @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/test/unity/conditions_test_unity.cpp b/src/test/unity/conditions_test_unity.cpp index 7e0e9760d..21032b06e 100644 --- a/src/test/unity/conditions_test_unity.cpp +++ b/src/test/unity/conditions_test_unity.cpp @@ -18,6 +18,3 @@ //============================================================================== #include -#include -#include -#include