Cryptoconditions: RSA-SHA-256 (RIPD-1213)

This commit is contained in:
Nik Bougalis
2016-09-03 18:11:58 -07:00
parent 83aa5517c0
commit d198b439fd
9 changed files with 786 additions and 0 deletions

View File

@@ -1800,12 +1800,18 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\conditions\impl\RsaSha256.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\ripple\conditions\impl\utils.h"> <ClInclude Include="..\..\src\ripple\conditions\impl\utils.h">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\PrefixSha256.h"> <ClInclude Include="..\..\src\ripple\conditions\PrefixSha256.h">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\PreimageSha256.h"> <ClInclude Include="..\..\src\ripple\conditions\PreimageSha256.h">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\RsaSha256.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\core\Config.h"> <ClInclude Include="..\..\src\ripple\core\Config.h">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\core\ConfigSections.h"> <ClInclude Include="..\..\src\ripple\core\ConfigSections.h">
@@ -4563,6 +4569,9 @@
<ClCompile Include="..\..\src\test\conditions\PreimageSha256_test.cpp"> <ClCompile Include="..\..\src\test\conditions\PreimageSha256_test.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild> <ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\test\conditions\RsaSha256_test.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\core\Config_test.cpp"> <ClCompile Include="..\..\src\test\core\Config_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>

View File

@@ -2439,6 +2439,9 @@
<ClCompile Include="..\..\src\ripple\conditions\impl\Fulfillment.cpp"> <ClCompile Include="..\..\src\ripple\conditions\impl\Fulfillment.cpp">
<Filter>ripple\conditions\impl</Filter> <Filter>ripple\conditions\impl</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\conditions\impl\RsaSha256.cpp">
<Filter>ripple\conditions\impl</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\conditions\impl\utils.h"> <ClInclude Include="..\..\src\ripple\conditions\impl\utils.h">
<Filter>ripple\conditions\impl</Filter> <Filter>ripple\conditions\impl</Filter>
</ClInclude> </ClInclude>
@@ -2448,6 +2451,9 @@
<ClInclude Include="..\..\src\ripple\conditions\PreimageSha256.h"> <ClInclude Include="..\..\src\ripple\conditions\PreimageSha256.h">
<Filter>ripple\conditions</Filter> <Filter>ripple\conditions</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\RsaSha256.h">
<Filter>ripple\conditions</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\core\Config.h"> <ClInclude Include="..\..\src\ripple\core\Config.h">
<Filter>ripple\core</Filter> <Filter>ripple\core</Filter>
</ClInclude> </ClInclude>
@@ -5346,6 +5352,9 @@
<ClCompile Include="..\..\src\test\conditions\PreimageSha256_test.cpp"> <ClCompile Include="..\..\src\test\conditions\PreimageSha256_test.cpp">
<Filter>test\conditions</Filter> <Filter>test\conditions</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\test\conditions\RsaSha256_test.cpp">
<Filter>test\conditions</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\core\Config_test.cpp"> <ClCompile Include="..\..\src\test\core\Config_test.cpp">
<Filter>test\core</Filter> <Filter>test\core</Filter>
</ClCompile> </ClCompile>

View File

@@ -150,6 +150,9 @@ validate (Condition const& c)
return (cf2 & definedFeatures) == cf2; return (cf2 & definedFeatures) == cf2;
} }
if (c.type == condition_rsa_sha256)
return (c.featureBitmask == (feature_rsa_pss | feature_sha256));
if (c.type == condition_ed25519) if (c.type == condition_ed25519)
return (c.featureBitmask == feature_ed25519); return (c.featureBitmask == feature_ed25519);

View File

@@ -0,0 +1,83 @@
//------------------------------------------------------------------------------
/*
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_RSA_SHA256_H
#define RIPPLE_CONDITIONS_RSA_SHA256_H
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/basics/Buffer.h>
#include <cstdint>
namespace ripple {
namespace cryptoconditions {
class RsaSha256 final
: public Fulfillment
{
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;
};
}
}
#endif

View File

@@ -21,6 +21,7 @@
#include <ripple/conditions/Fulfillment.h> #include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/PreimageSha256.h> #include <ripple/conditions/PreimageSha256.h>
#include <ripple/conditions/PrefixSha256.h> #include <ripple/conditions/PrefixSha256.h>
#include <ripple/conditions/RsaSha256.h>
#include <ripple/conditions/Ed25519.h> #include <ripple/conditions/Ed25519.h>
#include <ripple/conditions/impl/utils.h> #include <ripple/conditions/impl/utils.h>
#include <boost/regex.hpp> #include <boost/regex.hpp>
@@ -85,6 +86,10 @@ loadFulfillment (std::uint16_t type, Slice payload)
p = std::make_unique<PrefixSha256>(); p = std::make_unique<PrefixSha256>();
break; break;
case condition_rsa_sha256:
p = std::make_unique<RsaSha256>();
break;
case condition_ed25519: case condition_ed25519:
p = std::make_unique<Ed25519>(); p = std::make_unique<Ed25519>();
break; break;

View File

@@ -0,0 +1,313 @@
//------------------------------------------------------------------------------
/*
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/RsaSha256.h>
#include <ripple/conditions/impl/base64.h>
#include <ripple/protocol/digest.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/sha.h>
#include <boost/algorithm/clamp.hpp>
#include <algorithm>
#include <iterator>
namespace ripple {
namespace cryptoconditions {
namespace detail {
struct rsa_deleter
{
void operator() (RSA* rsa) const
{
RSA_free (rsa);
}
};
using RsaKey = std::unique_ptr<RSA, rsa_deleter>;
struct bn_deleter
{
void operator() (BIGNUM* bn) const
{
BN_free (bn);
}
};
using BigNum = std::unique_ptr<BIGNUM, bn_deleter>;
// 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<sha256_hasher::result_type>(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<sha256_hasher::result_type>(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);
// 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 whether the
// signature and modulus consist of the same number
// of octets and that the signature is numerically
// less than the modulus:
if (BN_num_bytes (sig.get()) != modBytes)
return false;
return BN_cmp (sig.get(), mod.get()) < 0;
}
}
Condition
RsaSha256::condition() const
{
std::vector<std::uint8_t> 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<sha256_hasher::result_type>(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<void*>(static_cast<void const*>(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;
}
}
}

View File

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

View File

@@ -0,0 +1,362 @@
//------------------------------------------------------------------------------
/*
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/RsaSha256.h>
#include <algorithm>
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"
"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\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<std::uint8_t, 3> aaa {{ 'a', 'a', 'a' }};
std::array<std::uint8_t, 3> 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);
}
}

View File

@@ -19,4 +19,5 @@
#include <test/conditions/PreimageSha256_test.cpp> #include <test/conditions/PreimageSha256_test.cpp>
#include <test/conditions/PrefixSha256_test.cpp> #include <test/conditions/PrefixSha256_test.cpp>
#include <test/conditions/RsaSha256_test.cpp>
#include <test/conditions/Ed25519_test.cpp> #include <test/conditions/Ed25519_test.cpp>