Cryptoconditions: ED25519 (RIPD-1214)

This commit is contained in:
Nik Bougalis
2016-07-11 22:24:24 -07:00
parent 8d0c93691d
commit 5711e7caa9
11 changed files with 448 additions and 0 deletions

View File

@@ -1782,6 +1782,8 @@
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\Condition.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\Ed25519.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\Fulfillment.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\impl\base64.h">
@@ -1790,6 +1792,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\conditions\impl\Ed25519.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\conditions\impl\Fulfillment.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
@@ -4546,6 +4552,9 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\conditions\Ed25519_test.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\conditions\PreimageSha256_test.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>

View File

@@ -2421,6 +2421,9 @@
<ClInclude Include="..\..\src\ripple\conditions\Condition.h">
<Filter>ripple\conditions</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\Ed25519.h">
<Filter>ripple\conditions</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\Fulfillment.h">
<Filter>ripple\conditions</Filter>
</ClInclude>
@@ -2430,6 +2433,9 @@
<ClCompile Include="..\..\src\ripple\conditions\impl\Condition.cpp">
<Filter>ripple\conditions\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\conditions\impl\Ed25519.cpp">
<Filter>ripple\conditions\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\conditions\impl\Fulfillment.cpp">
<Filter>ripple\conditions\impl</Filter>
</ClCompile>
@@ -5328,6 +5334,9 @@
<ClCompile Include="..\..\src\test\beast\SemanticVersion_test.cpp">
<Filter>test\beast</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\conditions\Ed25519_test.cpp">
<Filter>test\conditions</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\conditions\PreimageSha256_test.cpp">
<Filter>test\conditions</Filter>
</ClCompile>

View File

@@ -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;
}

View File

@@ -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 <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/protocol/PublicKey.h>
#include <ripple/protocol/SecretKey.h>
#include <array>
#include <cstdint>
namespace ripple {
namespace cryptoconditions {
class Ed25519 final
: public Fulfillment
{
static std::size_t constexpr signature_size_ = 64;
static std::size_t constexpr pubkey_size_ = 32;
std::array<std::uint8_t,
pubkey_size_ + signature_size_> payload_;
public:
Ed25519 () = default;
/** Create a fulfillment given a keypair and the message */
Ed25519 (
SecretKey const& secretKey,
PublicKey const& publicKey,
Slice message);
/** Create a fulfillment given a secret key and the message */
Ed25519 (
SecretKey const& secretKey,
Slice message);
Condition
condition() const override;
std::uint16_t
type () const override
{
return condition_ed25519;
}
std::uint32_t
features () const override
{
return feature_ed25519;
}
bool
ok () const override
{
return true;
}
std::size_t
payloadSize () const override
{
return payload_.size();
}
Buffer
payload() const override
{
return { payload_.data(), payload_.size() };
}
bool
validate (Slice data) const override;
bool
parsePayload (Slice s) override;
};
}
}
#endif

View File

@@ -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 <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/Ed25519.h>
#include <ripple/conditions/impl/base64.h>
#include <ripple/basics/contract.h>
#include <ed25519-donna/ed25519.h>
namespace ripple {
namespace cryptoconditions {
Ed25519::Ed25519 (
SecretKey const& secretKey,
PublicKey const& publicKey,
Slice message)
{
if (publicKeyType (publicKey) != KeyType::ed25519)
LogicError ("An Ed25519 public key is required.");
// When PublicKey wraps an Ed25519 key it prefixes
// the key itself with a 0xED byte. We carefully
// skip that byte.
std::memcpy (
payload_.data(),
publicKey.data() + 1,
publicKey.size() - 1);
// Now sign:
ed25519_sign (
message.data(),
message.size(),
secretKey.data(),
payload_.data(),
payload_.data() + pubkey_size_);
}
/** Create a fulfillment given a secret key and the message */
Ed25519::Ed25519 (
SecretKey const& secretKey,
Slice message)
{
// First derive the public key, and place it in the
// payload:
ed25519_publickey (
secretKey.data(),
payload_.data());
ed25519_sign (
message.data(),
message.size(),
secretKey.data(),
payload_.data(),
payload_.data() + pubkey_size_);
}
Condition
Ed25519::condition() const
{
Condition cc;
cc.type = type();
cc.featureBitmask = features();
cc.maxFulfillmentLength = payloadSize();
std::memcpy (
cc.fingerprint.data(),
payload_.data(),
pubkey_size_);
return cc;
}
bool
Ed25519::validate (Slice data) const
{
return ed25519_sign_open (
data.data(),
data.size(),
payload_.data(),
payload_.data() + pubkey_size_) == 0;
}
bool
Ed25519::parsePayload (Slice s)
{
// The payload consists of 96 consecutive bytes:
// The public key is the first 32 and the
// remaining 64 bytes are the signature.
if (s.size() != sizeof(payload_))
return false;
std::memcpy (payload_.data(), s.data(), s.size());
return true;
}
}
}

View File

@@ -20,6 +20,7 @@
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/PreimageSha256.h>
#include <ripple/conditions/Ed25519.h>
#include <ripple/conditions/impl/utils.h>
#include <boost/regex.hpp>
#include <boost/optional.hpp>
@@ -79,6 +80,10 @@ loadFulfillment (std::uint16_t type, Slice payload)
p = std::make_unique<PreimageSha256>();
break;
case condition_ed25519:
p = std::make_unique<Ed25519>();
break;
default:
throw std::domain_error (
"Unknown cryptocondition type " +

View File

@@ -43,6 +43,7 @@ public:
~SecretKey();
SecretKey (std::array<std::uint8_t, 32> const& data);
SecretKey (Slice const& slice);
std::uint8_t const*

View File

@@ -36,6 +36,11 @@ SecretKey::~SecretKey()
beast::secure_erase(buf_, sizeof(buf_));
}
SecretKey::SecretKey (std::array<std::uint8_t, 32> const& key)
{
std::memcpy(buf_, key.data(), key.size());
}
SecretKey::SecretKey (Slice const& slice)
{
if (slice.size() != sizeof(buf_))

View File

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

View File

@@ -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 <ripple/basics/strHex.h>
#include <ripple/beast/unit_test.h>
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/Ed25519.h>
#include <ripple/protocol/PublicKey.h>
#include <ripple/protocol/SecretKey.h>
#include <vector>
namespace ripple {
namespace cryptoconditions {
class Ed25519_test : public beast::unit_test::suite
{
void
check (
std::array<std::uint8_t, 32> const& secretKey,
std::vector<std::uint8_t> const& message,
std::string const& fulfillment,
std::string const& condition)
{
SecretKey const sk { makeSlice (secretKey) };
PublicKey const pk = derivePublicKey (KeyType::ed25519, sk);
auto f = loadFulfillment (fulfillment);
auto c = loadCondition (condition);
BEAST_EXPECT (f);
BEAST_EXPECT (c);
if (f && c)
{
// Ensure that loading works correctly
BEAST_EXPECT (to_string (*f) == fulfillment);
BEAST_EXPECT (to_string (*c) == condition);
// Ensures that the fulfillment generates
// the condition correctly:
BEAST_EXPECT (f->condition() == c);
// Check fulfillment
BEAST_EXPECT (validate (*f, *c, makeSlice(message)));
// Check correct creation of fulfillment
BEAST_EXPECT (*f == Ed25519 (sk, pk, makeSlice(message)));
}
}
void testKnownVectors ()
{
testcase ("Known Vectors");
std::array<std::uint8_t, 32> sk =
{{
0x50, 0xd8, 0x58, 0xe0, 0x98, 0x5e, 0xcc, 0x7f,
0x60, 0x41, 0x8a, 0xaf, 0x0c, 0xc5, 0xab, 0x58,
0x7f, 0x42, 0xc2, 0x57, 0x0a, 0x88, 0x40, 0x95,
0xa9, 0xe8, 0xcc, 0xac, 0xd0, 0xf6, 0x54, 0x5c
}};
std::vector<std::uint8_t> const payload (512, 0x21);
check (sk, payload,
"cf:4:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVGfTbzglso5Uo3i2O2WVP6abH1dz5k0H5DLylizTeL5UC0VSptUN4VCkhtbwx3B00pCeWNy1H78rq6OTXzok-EH",
"cc:4:20:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVE:96");
sk.fill (0x00);
check (sk, hexblob (""),
"cf:4:O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2imPiVs8r-LJUGA50OKmY4JWgARnT-jSN3hQkuQNaq9IPk_GAWhwXzHxAVlhOM4hqjV8DTKgZPQj3D7kqjq_U_gD",
"cc:4:20:O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik:96");
sk.fill (0xff);
check (sk, hexblob ("616263"),
"cf:4:dqFZIESm5PURJlvKc6YE2QsFKdHfYCvjChmpJXZg0fWuxqtqkSKv8PfcuWZ_9hMTaJRzK254wm9bZzEB4mf-Litl-k1T2tR4oa2mTVD9Hf232Ukg3D4aVkpkexy6NWAB",
"cc:4:20:dqFZIESm5PURJlvKc6YE2QsFKdHfYCvjChmpJXZg0fU:96");
}
void testFulfillment ()
{
testcase ("Fulfillment");
std::array<std::uint8_t, 32> sk =
{{
0x50, 0xd8, 0x58, 0xe0, 0x98, 0x5e, 0xcc, 0x7f,
0x60, 0x41, 0x8a, 0xaf, 0x0c, 0xc5, 0xab, 0x58,
0x7f, 0x42, 0xc2, 0x57, 0x0a, 0x88, 0x40, 0x95,
0xa9, 0xe8, 0xcc, 0xac, 0xd0, 0xf6, 0x54, 0x5c
}};
std::vector<std::uint8_t> const v1 (512, 0x21);
std::vector<std::uint8_t> const v2 (512, 0x22);
Ed25519 const f ({ sk }, makeSlice(v1));
// First check against incorrect conditions:
char const* const ccs[] =
{
"cc:0:3:PWh2oBRt6FdusjlahY3hIT0bksZbd53zozHP1aRYRUY:256",
"cc:1:25:XkflBmyISKuevH8-850LuMrzN-HT1Ds9zKUEzaZ2Wk0:103",
"cc:2:2b:d3O4epRCo_3rj17Bf3v8hp5ig7vq84ivPok07T9Rdl0:146",
"cc:3:11:uKkFs6dhGZCwD51c69vVvHYSp25cRi9IlvXfFaxhMjo:518",
"cc:4:20:O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik:96"
};
for (auto cc : ccs)
{
auto c = loadCondition (cc);
if (BEAST_EXPECT (c))
{
BEAST_EXPECT (! validate (f, c.get(), makeSlice(v1)));
BEAST_EXPECT (! validate (f, c.get(), makeSlice(v2)));
}
}
// Now, finally, check the correct condition:
auto c = loadCondition (
"cc:4:20:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVE:96");
if (BEAST_EXPECT (c))
{
BEAST_EXPECT (validate (f, c.get(), makeSlice(v1)));
BEAST_EXPECT (! validate (f, c.get(), makeSlice(v2)));
}
// Under the existing spec, multiple messages sharing
// the same key should generate the same fulfillment:
{
Ed25519 const f1 ({ sk }, makeSlice (v1));
Ed25519 const f2 ({ sk }, makeSlice (v2));
BEAST_EXPECT (f1.condition () == f2.condition ());
}
}
void testMalformedCondition ()
{
testcase ("Malformed Condition");
// This is malformed and will not load because a
// feature suite of 0 is not supported.
auto c1 = loadCondition (
"cc:4:0:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVE:96");
BEAST_EXPECT (!c1);
// The following will load but fail in different ways
auto c2 = loadCondition ( // only sha256
"cc:4:1:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVE:96");
BEAST_EXPECT (c2 && !validate(*c2));
auto c3 = loadCondition ( // only preimage
"cc:4:2:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVE:96");
BEAST_EXPECT (c3 && !validate(*c3));
auto c4 = loadCondition ( // sha256+preimage
"cc:4:3:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVE:96");
BEAST_EXPECT (c4 && !validate(*c4));
auto c5 = loadCondition ( // Ed25519+sha256+preimage
"cc:1:23:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c5 && !validate(*c5));
auto c6 = loadCondition ( // Ed25519+threshold
"cc:1:28:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c6 && !validate(*c6));
}
void run ()
{
testKnownVectors ();
testFulfillment ();
testMalformedCondition ();
}
};
BEAST_DEFINE_TESTSUITE (Ed25519, conditions, ripple);
}
}

View File

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