diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj
index 437cda3e4c..526470a6d3 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj
+++ b/Builds/VisualStudio2015/RippleD.vcxproj
@@ -1782,6 +1782,8 @@
+
+
@@ -1790,6 +1792,10 @@
True
True
+
+ True
+ True
+
True
True
@@ -4546,6 +4552,9 @@
True
True
+
+ True
+
True
diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters
index c2c4a4a0ca..52ba9c06e8 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters
@@ -2421,6 +2421,9 @@
ripple\conditions
+
+ ripple\conditions
+
ripple\conditions
@@ -2430,6 +2433,9 @@
ripple\conditions\impl
+
+ ripple\conditions\impl
+
ripple\conditions\impl
@@ -5328,6 +5334,9 @@
test\beast
+
+ test\conditions
+
test\conditions
diff --git a/src/ripple/conditions/Condition.h b/src/ripple/conditions/Condition.h
index 1d48329765..e36ed25c7c 100644
--- a/src/ripple/conditions/Condition.h
+++ b/src/ripple/conditions/Condition.h
@@ -127,6 +127,9 @@ validate (Condition const& c)
if (c.type == condition_hashlock)
return (c.featureBitmask == (feature_sha256 | feature_preimage));
+ if (c.type == condition_ed25519)
+ return (c.featureBitmask == feature_ed25519);
+
return false;
}
diff --git a/src/ripple/conditions/Ed25519.h b/src/ripple/conditions/Ed25519.h
new file mode 100644
index 0000000000..d126b425dd
--- /dev/null
+++ b/src/ripple/conditions/Ed25519.h
@@ -0,0 +1,100 @@
+//------------------------------------------------------------------------------
+/*
+ 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/impl/Ed25519.cpp b/src/ripple/conditions/impl/Ed25519.cpp
new file mode 100644
index 0000000000..59d6dc9fbc
--- /dev/null
+++ b/src/ripple/conditions/impl/Ed25519.cpp
@@ -0,0 +1,115 @@
+//------------------------------------------------------------------------------
+/*
+ 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 3a866021a9..b04a7b0716 100644
--- a/src/ripple/conditions/impl/Fulfillment.cpp
+++ b/src/ripple/conditions/impl/Fulfillment.cpp
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -79,6 +80,10 @@ loadFulfillment (std::uint16_t type, Slice payload)
p = std::make_unique();
break;
+ case condition_ed25519:
+ p = std::make_unique();
+ break;
+
default:
throw std::domain_error (
"Unknown cryptocondition type " +
diff --git a/src/ripple/protocol/SecretKey.h b/src/ripple/protocol/SecretKey.h
index 384ddedbda..91e4210442 100644
--- a/src/ripple/protocol/SecretKey.h
+++ b/src/ripple/protocol/SecretKey.h
@@ -43,6 +43,7 @@ public:
~SecretKey();
+ SecretKey (std::array const& data);
SecretKey (Slice const& slice);
std::uint8_t const*
diff --git a/src/ripple/protocol/impl/SecretKey.cpp b/src/ripple/protocol/impl/SecretKey.cpp
index c481e21f81..4ea7917c73 100644
--- a/src/ripple/protocol/impl/SecretKey.cpp
+++ b/src/ripple/protocol/impl/SecretKey.cpp
@@ -36,6 +36,11 @@ SecretKey::~SecretKey()
beast::secure_erase(buf_, sizeof(buf_));
}
+SecretKey::SecretKey (std::array const& key)
+{
+ std::memcpy(buf_, key.data(), key.size());
+}
+
SecretKey::SecretKey (Slice const& slice)
{
if (slice.size() != sizeof(buf_))
diff --git a/src/ripple/unity/conditions.cpp b/src/ripple/unity/conditions.cpp
index 41bb21824a..a6cb3af12e 100644
--- a/src/ripple/unity/conditions.cpp
+++ b/src/ripple/unity/conditions.cpp
@@ -21,3 +21,4 @@
#include
#include
+#include
diff --git a/src/test/conditions/Ed25519_test.cpp b/src/test/conditions/Ed25519_test.cpp
new file mode 100644
index 0000000000..b314b89ad9
--- /dev/null
+++ b/src/test/conditions/Ed25519_test.cpp
@@ -0,0 +1,199 @@
+//------------------------------------------------------------------------------
+/*
+ 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/unity/conditions_test_unity.cpp b/src/unity/conditions_test_unity.cpp
index 21032b06e5..2387ee7e63 100644
--- a/src/unity/conditions_test_unity.cpp
+++ b/src/unity/conditions_test_unity.cpp
@@ -18,3 +18,4 @@
//==============================================================================
#include
+#include