mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-07 02:36:47 +00:00
Move xrpld/conditions to libxrpl
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
This commit is contained in:
98
include/xrpl/conditions/Condition.h
Normal file
98
include/xrpl/conditions/Condition.h
Normal file
@@ -0,0 +1,98 @@
|
||||
#ifndef XRPL_CONDITIONS_CONDITION_H
|
||||
#define XRPL_CONDITIONS_CONDITION_H
|
||||
|
||||
#include <xrpl/basics/Buffer.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/conditions/detail/utils.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
|
||||
namespace xrpl {
|
||||
namespace cryptoconditions {
|
||||
|
||||
enum class Type : std::uint8_t {
|
||||
preimageSha256 = 0,
|
||||
prefixSha256 = 1,
|
||||
thresholdSha256 = 2,
|
||||
rsaSha256 = 3,
|
||||
ed25519Sha256 = 4
|
||||
};
|
||||
|
||||
class Condition
|
||||
{
|
||||
public:
|
||||
/** The largest binary condition we support.
|
||||
|
||||
@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.
|
||||
*/
|
||||
static constexpr std::size_t maxSerializedCondition = 128;
|
||||
|
||||
/** Load a condition from its binary form
|
||||
|
||||
@param s The buffer containing the fulfillment to load.
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
The binary format for a condition is specified in the
|
||||
cryptoconditions RFC. See:
|
||||
|
||||
https://tools.ietf.org/html/draft-thomas-crypto-conditions-02#section-7.2
|
||||
*/
|
||||
static std::unique_ptr<Condition>
|
||||
deserialize(Slice s, std::error_code& ec);
|
||||
|
||||
public:
|
||||
Type type;
|
||||
|
||||
/** An identifier for this condition.
|
||||
|
||||
This fingerprint is meant to be unique only with
|
||||
respect to other conditions of the same type.
|
||||
*/
|
||||
Buffer fingerprint;
|
||||
|
||||
/** The cost associated with this condition. */
|
||||
std::uint32_t cost;
|
||||
|
||||
/** For compound conditions, set of conditions includes */
|
||||
std::set<Type> subtypes;
|
||||
|
||||
Condition(Type t, std::uint32_t c, Slice fp)
|
||||
: type(t), fingerprint(fp), cost(c)
|
||||
{
|
||||
}
|
||||
|
||||
Condition(Type t, std::uint32_t c, Buffer&& fp)
|
||||
: type(t), fingerprint(std::move(fp)), cost(c)
|
||||
{
|
||||
}
|
||||
|
||||
~Condition() = default;
|
||||
|
||||
Condition(Condition const&) = default;
|
||||
Condition(Condition&&) = default;
|
||||
|
||||
Condition() = delete;
|
||||
};
|
||||
|
||||
inline bool
|
||||
operator==(Condition const& lhs, Condition const& rhs)
|
||||
{
|
||||
return lhs.type == rhs.type && lhs.cost == rhs.cost &&
|
||||
lhs.subtypes == rhs.subtypes && lhs.fingerprint == rhs.fingerprint;
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator!=(Condition const& lhs, Condition const& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace cryptoconditions
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
126
include/xrpl/conditions/Fulfillment.h
Normal file
126
include/xrpl/conditions/Fulfillment.h
Normal file
@@ -0,0 +1,126 @@
|
||||
#ifndef XRPL_CONDITIONS_FULFILLMENT_H
|
||||
#define XRPL_CONDITIONS_FULFILLMENT_H
|
||||
|
||||
#include <xrpl/basics/Buffer.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/conditions/Condition.h>
|
||||
|
||||
namespace xrpl {
|
||||
namespace cryptoconditions {
|
||||
|
||||
struct Fulfillment
|
||||
{
|
||||
public:
|
||||
/** The largest binary fulfillment we support.
|
||||
|
||||
@note This value will be increased in the future, but it
|
||||
must never decrease, as that could cause fulfillments
|
||||
that were previously considered valid to no longer
|
||||
be allowed.
|
||||
*/
|
||||
static constexpr std::size_t maxSerializedFulfillment = 256;
|
||||
|
||||
/** Load a fulfillment from its binary form
|
||||
|
||||
@param s The buffer containing the fulfillment to load.
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
The binary format for a fulfillment is specified in the
|
||||
cryptoconditions RFC. See:
|
||||
|
||||
https://tools.ietf.org/html/draft-thomas-crypto-conditions-02#section-7.3
|
||||
*/
|
||||
static std::unique_ptr<Fulfillment>
|
||||
deserialize(Slice s, std::error_code& ec);
|
||||
|
||||
public:
|
||||
virtual ~Fulfillment() = default;
|
||||
|
||||
/** 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
|
||||
fingerprint() const = 0;
|
||||
|
||||
/** Returns the type of this condition. */
|
||||
virtual Type
|
||||
type() const = 0;
|
||||
|
||||
/** Validates a fulfillment. */
|
||||
virtual bool
|
||||
validate(Slice data) const = 0;
|
||||
|
||||
/** 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 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.cost() == rhs.cost() &&
|
||||
lhs.fingerprint() == rhs.fingerprint();
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator!=(Fulfillment const& lhs, Fulfillment const& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/** Determine whether the given fulfillment and condition match */
|
||||
bool
|
||||
match(Fulfillment const& f, Condition const& c);
|
||||
|
||||
/** Verify if the given message satisfies the fulfillment.
|
||||
|
||||
@param f The fulfillment
|
||||
@param c The condition
|
||||
@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(Fulfillment const& f, Condition const& c, Slice m);
|
||||
|
||||
/** Verify a cryptoconditional trigger.
|
||||
|
||||
A cryptoconditional trigger is a cryptocondition with
|
||||
an empty message.
|
||||
|
||||
When using such triggers, it is recommended that the
|
||||
trigger be of type preimage, prefix or threshold. If
|
||||
a signature type is used (i.e. Ed25519 or RSA-SHA256)
|
||||
then the Ed25519 or RSA keys should be single-use keys.
|
||||
|
||||
@param f The fulfillment
|
||||
@param c The condition
|
||||
*/
|
||||
bool
|
||||
validate(Fulfillment const& f, Condition const& c);
|
||||
|
||||
} // namespace cryptoconditions
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
134
include/xrpl/conditions/detail/PreimageSha256.h
Normal file
134
include/xrpl/conditions/detail/PreimageSha256.h
Normal file
@@ -0,0 +1,134 @@
|
||||
#ifndef XRPL_CONDITIONS_PREIMAGE_SHA256_H
|
||||
#define XRPL_CONDITIONS_PREIMAGE_SHA256_H
|
||||
|
||||
#include <xrpl/basics/Buffer.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/conditions/Condition.h>
|
||||
#include <xrpl/conditions/Fulfillment.h>
|
||||
#include <xrpl/conditions/detail/error.h>
|
||||
#include <xrpl/protocol/digest.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace xrpl {
|
||||
namespace cryptoconditions {
|
||||
|
||||
class PreimageSha256 final : public Fulfillment
|
||||
{
|
||||
public:
|
||||
/** The maximum allowed length of a preimage.
|
||||
|
||||
The specification does not specify a minimum supported
|
||||
length, nor does it require all conditions to support
|
||||
the same minimum length.
|
||||
|
||||
While future versions of this code will never lower
|
||||
this limit, they may opt to raise it.
|
||||
*/
|
||||
static constexpr std::size_t maxPreimageLength = 128;
|
||||
|
||||
/** Parse the payload for a PreimageSha256 condition
|
||||
|
||||
@param s A slice containing the DER encoded payload
|
||||
@param ec indicates success or failure of the operation
|
||||
@return the preimage, if successful; empty pointer otherwise.
|
||||
*/
|
||||
static std::unique_ptr<Fulfillment>
|
||||
deserialize(Slice s, std::error_code& ec)
|
||||
{
|
||||
// Per the RFC, a preimage fulfulliment is defined as
|
||||
// follows:
|
||||
//
|
||||
// PreimageFulfillment ::= SEQUENCE {
|
||||
// preimage OCTET STRING
|
||||
// }
|
||||
|
||||
using namespace der;
|
||||
|
||||
auto p = parsePreamble(s, ec);
|
||||
if (ec)
|
||||
return nullptr;
|
||||
|
||||
if (!isPrimitive(p) || !isContextSpecific(p))
|
||||
{
|
||||
ec = error::incorrect_encoding;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (p.tag != 0)
|
||||
{
|
||||
ec = error::unexpected_tag;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (s.size() != p.length)
|
||||
{
|
||||
ec = error::trailing_garbage;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (s.size() > maxPreimageLength)
|
||||
{
|
||||
ec = error::preimage_too_long;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto b = parseOctetString(s, p.length, ec);
|
||||
if (ec)
|
||||
return {};
|
||||
|
||||
return std::make_unique<PreimageSha256>(std::move(b));
|
||||
}
|
||||
|
||||
private:
|
||||
Buffer payload_;
|
||||
|
||||
public:
|
||||
PreimageSha256(Buffer&& b) noexcept : payload_(std::move(b))
|
||||
{
|
||||
}
|
||||
|
||||
PreimageSha256(Slice s) noexcept : payload_(s)
|
||||
{
|
||||
}
|
||||
|
||||
Type
|
||||
type() const override
|
||||
{
|
||||
return Type::preimageSha256;
|
||||
}
|
||||
|
||||
Buffer
|
||||
fingerprint() const override
|
||||
{
|
||||
sha256_hasher h;
|
||||
h(payload_.data(), payload_.size());
|
||||
auto const d = static_cast<sha256_hasher::result_type>(h);
|
||||
return {d.data(), d.size()};
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
cost() const override
|
||||
{
|
||||
return static_cast<std::uint32_t>(payload_.size());
|
||||
}
|
||||
|
||||
Condition
|
||||
condition() const override
|
||||
{
|
||||
return {type(), cost(), fingerprint()};
|
||||
}
|
||||
|
||||
bool
|
||||
validate(Slice) const override
|
||||
{
|
||||
// Perhaps counterintuitively, the message isn't
|
||||
// relevant.
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace cryptoconditions
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
47
include/xrpl/conditions/detail/error.h
Normal file
47
include/xrpl/conditions/detail/error.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef XRPL_CONDITIONS_ERROR_H
|
||||
#define XRPL_CONDITIONS_ERROR_H
|
||||
|
||||
#include <system_error>
|
||||
|
||||
namespace xrpl {
|
||||
namespace cryptoconditions {
|
||||
|
||||
enum class error {
|
||||
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);
|
||||
|
||||
} // namespace cryptoconditions
|
||||
} // namespace xrpl
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct is_error_code_enum<xrpl::cryptoconditions::error>
|
||||
{
|
||||
explicit is_error_code_enum() = default;
|
||||
|
||||
static bool const value = true;
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif
|
||||
212
include/xrpl/conditions/detail/utils.h
Normal file
212
include/xrpl/conditions/detail/utils.h
Normal file
@@ -0,0 +1,212 @@
|
||||
#ifndef XRPL_CONDITIONS_UTILS_H
|
||||
#define XRPL_CONDITIONS_UTILS_H
|
||||
|
||||
#include <xrpl/basics/Buffer.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/conditions/detail/error.h>
|
||||
|
||||
#include <boost/dynamic_bitset.hpp>
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace xrpl {
|
||||
namespace cryptoconditions {
|
||||
|
||||
// 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
|
||||
{
|
||||
explicit Preamble() = default;
|
||||
std::uint8_t type = 0;
|
||||
std::size_t tag = 0;
|
||||
std::size_t length = 0;
|
||||
};
|
||||
|
||||
inline bool
|
||||
isPrimitive(Preamble const& p)
|
||||
{
|
||||
return (p.type & 0x20) == 0;
|
||||
}
|
||||
|
||||
inline bool
|
||||
isConstructed(Preamble const& p)
|
||||
{
|
||||
return !isPrimitive(p);
|
||||
}
|
||||
|
||||
inline bool
|
||||
isUniversal(Preamble const& p)
|
||||
{
|
||||
return (p.type & 0xC0) == 0;
|
||||
}
|
||||
|
||||
inline bool
|
||||
isApplication(Preamble const& p)
|
||||
{
|
||||
return (p.type & 0xC0) == 0x40;
|
||||
}
|
||||
|
||||
inline bool
|
||||
isContextSpecific(Preamble const& p)
|
||||
{
|
||||
return (p.type & 0xC0) == 0x80;
|
||||
}
|
||||
|
||||
inline bool
|
||||
isPrivate(Preamble const& p)
|
||||
{
|
||||
return (p.type & 0xC0) == 0xC0;
|
||||
}
|
||||
|
||||
inline Preamble
|
||||
parsePreamble(Slice& s, std::error_code& ec)
|
||||
{
|
||||
Preamble p;
|
||||
|
||||
if (s.size() < 2)
|
||||
{
|
||||
ec = error::short_preamble;
|
||||
return p;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
ec = error::malformed_encoding;
|
||||
return p;
|
||||
}
|
||||
|
||||
if (cnt > sizeof(std::size_t))
|
||||
{
|
||||
ec = error::large_size;
|
||||
return p;
|
||||
}
|
||||
|
||||
if (cnt > s.size())
|
||||
{
|
||||
ec = error::short_preamble;
|
||||
return p;
|
||||
}
|
||||
|
||||
p.length = 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
inline Buffer
|
||||
parseOctetString(Slice& s, std::uint32_t count, std::error_code& ec)
|
||||
{
|
||||
if (count > s.size())
|
||||
{
|
||||
ec = error::buffer_underfull;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (count > 65535)
|
||||
{
|
||||
ec = error::large_size;
|
||||
return {};
|
||||
}
|
||||
|
||||
Buffer b(s.data(), count);
|
||||
s += count;
|
||||
return b;
|
||||
}
|
||||
|
||||
template <class Integer>
|
||||
Integer
|
||||
parseInteger(Slice& s, std::size_t count, std::error_code& ec)
|
||||
{
|
||||
Integer v{0};
|
||||
|
||||
if (s.empty())
|
||||
{
|
||||
// can never have zero sized integers
|
||||
ec = error::malformed_encoding;
|
||||
return v;
|
||||
}
|
||||
|
||||
if (count > s.size())
|
||||
{
|
||||
ec = error::buffer_underfull;
|
||||
return v;
|
||||
}
|
||||
|
||||
bool const isSigned = std::numeric_limits<Integer>::is_signed;
|
||||
// unsigned types may have a leading zero octet
|
||||
size_t const 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;
|
||||
}
|
||||
|
||||
} // namespace der
|
||||
} // namespace cryptoconditions
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user