diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj
index c35972e96..863e8dceb 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj
+++ b/Builds/VisualStudio2015/RippleD.vcxproj
@@ -3854,6 +3854,10 @@
+
+
+
+
@@ -3864,6 +3868,10 @@
+
+
+
+
@@ -3894,6 +3902,10 @@
+
+
+
+
@@ -3914,6 +3926,10 @@
+
+
+
+
True
diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters
index 8cd6e8f9e..d5d7e100e 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters
@@ -370,6 +370,15 @@
{E25BE380-48B7-7EA9-DFD6-F38F1E8A22FF}
+
+ {6BE34C70-DCAB-96D1-487C-ADC692DA720B}
+
+
+ {46FCBB68-FE6A-0EB7-98C9-C695B05E6503}
+
+
+ {0B56B4A9-D9BC-B7FB-DD09-ADAF9DDE4895}
+
{26306562-F81D-B6CD-B192-22BA51E1A96B}
@@ -4521,6 +4530,12 @@
secp256k1\include
+
+ secp256k1\include
+
+
+ secp256k1\include
+
secp256k1\src
@@ -4536,6 +4551,12 @@
secp256k1\src
+
+ secp256k1\src
+
+
+ secp256k1\src
+
secp256k1\src
@@ -4581,6 +4602,12 @@
secp256k1\src
+
+ secp256k1\src\modules\ecdh
+
+
+ secp256k1\src\modules\recovery
+
secp256k1\src
@@ -4611,6 +4638,12 @@
secp256k1\src
+
+ secp256k1\src
+
+
+ secp256k1\src
+
secp256k1\src
diff --git a/src/ripple/protocol/PublicKey.h b/src/ripple/protocol/PublicKey.h
index 394723b13..e4ec47216 100644
--- a/src/ripple/protocol/PublicKey.h
+++ b/src/ripple/protocol/PublicKey.h
@@ -29,6 +29,7 @@
#include
#include
#include
+#include
#include
namespace ripple {
@@ -98,6 +99,11 @@ public:
}
};
+/** Print the public key to a stream.
+*/
+std::ostream&
+operator<<(std::ostream& os, PublicKey const& pk);
+
inline
bool
operator== (PublicKey const& lhs,
diff --git a/src/ripple/protocol/SecretKey.h b/src/ripple/protocol/SecretKey.h
index 91e421044..20f0e1c6b 100644
--- a/src/ripple/protocol/SecretKey.h
+++ b/src/ripple/protocol/SecretKey.h
@@ -27,6 +27,7 @@
#include
#include
#include
+#include
namespace ripple {
@@ -57,6 +58,14 @@ public:
{
return sizeof(buf_);
}
+
+ /** Convert the secret key to a hexadecimal string.
+
+ @note The operator<< function is deliberately omitted
+ to avoid accidental exposure of secret key material.
+ */
+ std::string
+ to_string() const;
};
//------------------------------------------------------------------------------
diff --git a/src/ripple/protocol/impl/PublicKey.cpp b/src/ripple/protocol/impl/PublicKey.cpp
index 872b8c394..11c09428f 100644
--- a/src/ripple/protocol/impl/PublicKey.cpp
+++ b/src/ripple/protocol/impl/PublicKey.cpp
@@ -22,6 +22,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -29,6 +30,13 @@
namespace ripple {
+std::ostream&
+operator<<(std::ostream& os, PublicKey const& pk)
+{
+ os << strHex(pk.data(), pk.size());
+ return os;
+}
+
using uint264 = boost::multiprecision::number<
boost::multiprecision::cpp_int_backend<
264, 264, boost::multiprecision::signed_magnitude,
@@ -219,17 +227,51 @@ verifyDigest (PublicKey const& publicKey,
{
if (publicKeyType(publicKey) != KeyType::secp256k1)
LogicError("sign: secp256k1 required for digest signing");
-
auto const canonicality = ecdsaCanonicality(sig);
if (! canonicality)
return false;
if (mustBeFullyCanonical &&
(*canonicality != ECDSACanonicality::fullyCanonical))
return false;
+
+ secp256k1_pubkey pubkey_imp;
+ if(secp256k1_ec_pubkey_parse(
+ secp256k1Context(),
+ &pubkey_imp,
+ reinterpret_cast(
+ publicKey.data()),
+ publicKey.size()) != 1)
+ return false;
+
+ secp256k1_ecdsa_signature sig_imp;
+ if(secp256k1_ecdsa_signature_parse_der(
+ secp256k1Context(),
+ &sig_imp,
+ reinterpret_cast(
+ sig.data()),
+ sig.size()) != 1)
+ return false;
+ if (*canonicality != ECDSACanonicality::fullyCanonical)
+ {
+ secp256k1_ecdsa_signature sig_norm;
+ if(secp256k1_ecdsa_signature_normalize(
+ secp256k1Context(),
+ &sig_norm,
+ &sig_imp) != 1)
+ return false;
+ return secp256k1_ecdsa_verify(
+ secp256k1Context(),
+ &sig_norm,
+ reinterpret_cast(
+ digest.data()),
+ &pubkey_imp) == 1;
+ }
return secp256k1_ecdsa_verify(
- secp256k1Context(), secpp(digest.data()),
- secpp(sig.data()), sig.size(),
- secpp(publicKey.data()), publicKey.size()) == 1;
+ secp256k1Context(),
+ &sig_imp,
+ reinterpret_cast(
+ digest.data()),
+ &pubkey_imp) == 1;
}
bool
diff --git a/src/ripple/protocol/impl/SecretKey.cpp b/src/ripple/protocol/impl/SecretKey.cpp
index 4ea7917c7..0a8dbca1e 100644
--- a/src/ripple/protocol/impl/SecretKey.cpp
+++ b/src/ripple/protocol/impl/SecretKey.cpp
@@ -18,6 +18,7 @@
//==============================================================================
#include
+#include
#include
#include
#include
@@ -48,6 +49,12 @@ SecretKey::SecretKey (Slice const& slice)
std::memcpy(buf_, slice.data(), sizeof(buf_));
}
+std::string
+SecretKey::to_string() const
+{
+ return strHex(data(), size());
+}
+
//------------------------------------------------------------------------------
Generator::Generator (Seed const& seed)
@@ -88,16 +95,29 @@ signDigest (PublicKey const& pk, SecretKey const& sk,
if (publicKeyType(pk.slice()) != KeyType::secp256k1)
LogicError("sign: secp256k1 required for digest signing");
- int siglen = 72;
- unsigned char sig[72];
- auto const result = secp256k1_ecdsa_sign(
- secp256k1Context(),
- digest.data(), sig, &siglen,
- sk.data(), secp256k1_nonce_function_rfc6979,
- nullptr);
- if (result != 1)
+ BOOST_ASSERT(sk.size() == 32);
+ secp256k1_ecdsa_signature sig_imp;
+ if(secp256k1_ecdsa_sign(
+ secp256k1Context(),
+ &sig_imp,
+ reinterpret_cast(
+ digest.data()),
+ reinterpret_cast(
+ sk.data()),
+ secp256k1_nonce_function_rfc6979,
+ nullptr) != 1)
LogicError("sign: secp256k1_ecdsa_sign failed");
- return Buffer(sig, siglen);
+
+ unsigned char sig[72];
+ size_t len = sizeof(sig);
+ if(secp256k1_ecdsa_signature_serialize_der(
+ secp256k1Context(),
+ sig,
+ &len,
+ &sig_imp) != 1)
+ LogicError("sign: secp256k1_ecdsa_signature_serialize_der failed");
+
+ return Buffer{sig, len};
}
Buffer
@@ -123,16 +143,29 @@ sign (PublicKey const& pk,
h(m.data(), m.size());
auto const digest =
sha512_half_hasher::result_type(h);
- int siglen = 72;
- unsigned char sig[72];
- auto const result = secp256k1_ecdsa_sign(
- secp256k1Context(),
- digest.data(), sig, &siglen,
- sk.data(), secp256k1_nonce_function_rfc6979,
- nullptr);
- if (result != 1)
+
+ secp256k1_ecdsa_signature sig_imp;
+ if(secp256k1_ecdsa_sign(
+ secp256k1Context(),
+ &sig_imp,
+ reinterpret_cast(
+ digest.data()),
+ reinterpret_cast(
+ sk.data()),
+ secp256k1_nonce_function_rfc6979,
+ nullptr) != 1)
LogicError("sign: secp256k1_ecdsa_sign failed");
- return Buffer(sig, siglen);
+
+ unsigned char sig[72];
+ size_t len = sizeof(sig);
+ if(secp256k1_ecdsa_signature_serialize_der(
+ secp256k1Context(),
+ sig,
+ &len,
+ &sig_imp) != 1)
+ LogicError("sign: secp256k1_ecdsa_signature_serialize_der failed");
+
+ return Buffer{sig, len};
}
default:
LogicError("sign: invalid type");
@@ -184,16 +217,26 @@ derivePublicKey (KeyType type, SecretKey const& sk)
{
case KeyType::secp256k1:
{
- int len;
- unsigned char buf[33];
- auto const result =
- secp256k1_ec_pubkey_create(
+ secp256k1_pubkey pubkey_imp;
+ if(secp256k1_ec_pubkey_create(
secp256k1Context(),
- buf, &len, sk.data(), 1);
- if (result != 1)
- LogicError("derivePublicKey: failure");
- return PublicKey(Slice{ buf,
- static_cast(len) });
+ &pubkey_imp,
+ reinterpret_cast(
+ sk.data())) != 1)
+ LogicError("derivePublicKey: secp256k1_ec_pubkey_create failed");
+
+ unsigned char pubkey[33];
+ size_t len = sizeof(pubkey);
+ if(secp256k1_ec_pubkey_serialize(
+ secp256k1Context(),
+ pubkey,
+ &len,
+ &pubkey_imp,
+ SECP256K1_EC_COMPRESSED) != 1)
+ LogicError("derivePublicKey: secp256k1_ec_pubkey_serialize failed");
+
+ return PublicKey{Slice{pubkey,
+ static_cast(len)}};
}
case KeyType::ed25519:
{
diff --git a/src/ripple/protocol/impl/secp256k1.h b/src/ripple/protocol/impl/secp256k1.h
index 92870601b..cc12d3d06 100644
--- a/src/ripple/protocol/impl/secp256k1.h
+++ b/src/ripple/protocol/impl/secp256k1.h
@@ -25,15 +25,15 @@
namespace ripple {
template
-secp256k1_context_t const*
+secp256k1_context const*
secp256k1Context()
{
struct holder
{
- secp256k1_context_t* impl;
+ secp256k1_context* impl;
holder()
: impl (secp256k1_context_create(
- SECP256K1_CONTEXT_VERIFY +
+ SECP256K1_CONTEXT_VERIFY |
SECP256K1_CONTEXT_SIGN))
{
}
@@ -47,13 +47,6 @@ secp256k1Context()
return h.impl;
}
-inline
-unsigned char const*
-secpp(void const* p)
-{
- return static_cast(p);
-}
-
} // ripple
#endif
diff --git a/src/test/protocol/SecretKey_test.cpp b/src/test/protocol/SecretKey_test.cpp
index e3aa7460e..7cc831396 100644
--- a/src/test/protocol/SecretKey_test.cpp
+++ b/src/test/protocol/SecretKey_test.cpp
@@ -28,6 +28,8 @@
#include
#include
+#include
+
namespace ripple {
class SecretKey_test : public beast::unit_test::suite
@@ -41,6 +43,183 @@ class SecretKey_test : public beast::unit_test::suite
}
public:
+ using blob = std::vector;
+
+ template
+ static
+ void
+ hex_to_binary (FwdIter first, FwdIter last, Container& out)
+ {
+ struct Table
+ {
+ int val[256];
+ Table ()
+ {
+ std::fill (val, val+256, 0);
+ for (int i = 0; i < 10; ++i)
+ val ['0'+i] = i;
+ for (int i = 0; i < 6; ++i)
+ {
+ val ['A'+i] = 10 + i;
+ val ['a'+i] = 10 + i;
+ }
+ }
+ int operator[] (int i)
+ {
+ return val[i];
+ }
+ };
+
+ static Table lut;
+ out.reserve (std::distance (first, last) / 2);
+ while (first != last)
+ {
+ auto const hi (lut[(*first++)]);
+ auto const lo (lut[(*first++)]);
+ out.push_back ((hi*16)+lo);
+ }
+ }
+
+ static
+ uint256
+ hex_to_digest(std::string const& s)
+ {
+ blob b;
+ hex_to_binary (s.begin (), s.end (), b);
+ return uint256::fromVoid(b.data());
+ }
+
+ static
+ PublicKey
+ hex_to_pk(std::string const& s)
+ {
+ blob b;
+ hex_to_binary (s.begin (), s.end (), b);
+ return PublicKey{Slice{b.data(), b.size()}};
+ }
+
+ static
+ SecretKey
+ hex_to_sk(std::string const& s)
+ {
+ blob b;
+ hex_to_binary (s.begin (), s.end (), b);
+ return SecretKey{Slice{b.data(), b.size()}};
+ }
+
+ static
+ Buffer
+ hex_to_sig(std::string const& s)
+ {
+ blob b;
+ hex_to_binary (s.begin (), s.end (), b);
+ return Buffer{Slice{b.data(), b.size()}};
+ }
+
+ // VFALCO We can remove this commented out code
+ // later, when we have confidence in the vectors.
+
+ /*
+ Buffer
+ makeNonCanonical(Buffer const& sig)
+ {
+ secp256k1_ecdsa_signature sigin;
+ BEAST_EXPECT(secp256k1_ecdsa_signature_parse_der(
+ secp256k1Context(),
+ &sigin,
+ reinterpret_cast(
+ sig.data()),
+ sig.size()) == 1);
+ secp256k1_ecdsa_signature sigout;
+ BEAST_EXPECT(secp256k1_ecdsa_signature_denormalize(
+ secp256k1Context(),
+ &sigout,
+ &sigin) == 1);
+ unsigned char buf[72];
+ size_t len = sizeof(buf);
+ BEAST_EXPECT(secp256k1_ecdsa_signature_serialize_der(
+ secp256k1Context(),
+ buf,
+ &len,
+ &sigout) == 1);
+ return Buffer{buf, len};
+ }
+
+ void
+ makeCanonicalityTestVectors()
+ {
+ uint256 digest;
+ beast::rngfill (
+ digest.data(),
+ digest.size(),
+ crypto_prng());
+ log << "digest " << strHex(digest.data(), digest.size()) << std::endl;
+
+ auto const sk = randomSecretKey();
+ auto const pk = derivePublicKey(KeyType::secp256k1, sk);
+ log << "public " << pk << std::endl;
+ log << "secret " << sk.to_string() << std::endl;
+
+ auto sig = signDigest(pk, sk, digest);
+ log << "canonical sig " << strHex(sig) << std::endl;
+
+ auto const non = makeNonCanonical(sig);
+ log << "non-canon sig " << strHex(non) << std::endl;
+
+ {
+ auto const canonicality = ecdsaCanonicality(sig);
+ BEAST_EXPECT(canonicality);
+ BEAST_EXPECT(*canonicality == ECDSACanonicality::fullyCanonical);
+ }
+
+ {
+ auto const canonicality = ecdsaCanonicality(non);
+ BEAST_EXPECT(canonicality);
+ BEAST_EXPECT(*canonicality != ECDSACanonicality::fullyCanonical);
+ }
+
+ BEAST_EXPECT(verifyDigest(pk, digest, sig, false));
+ BEAST_EXPECT(verifyDigest(pk, digest, sig, true));
+ BEAST_EXPECT(verifyDigest(pk, digest, non, false));
+ BEAST_EXPECT(! verifyDigest(pk, digest, non, true));
+ }
+ */
+
+ // Ensure that verification does the right thing with
+ // respect to the matrix of canonicality variables.
+ void
+ testCanonicality()
+ {
+ testcase ("secp256k1 canonicality");
+
+#if 0
+ makeCanonicalityTestVectors();
+#else
+ auto const digest = hex_to_digest("34C19028C80D21F3F48C9354895F8D5BF0D5EE7FF457647CF655F5530A3022A7");
+ auto const pk = hex_to_pk("025096EB12D3E924234E7162369C11D8BF877EDA238778E7A31FF0AAC5D0DBCF37");
+ auto const sk = hex_to_sk("AA921417E7E5C299DA4EEC16D1CAA92F19B19F2A68511F68EC73BBB2F5236F3D");
+ auto const sig = hex_to_sig("3045022100B49D07F0E934BA468C0EFC78117791408D1FB8B63A6492AD395AC2F360F246600220508739DB0A2EF81676E39F459C8BBB07A09C3E9F9BEB696294D524D479D62740");
+ auto const non = hex_to_sig("3046022100B49D07F0E934BA468C0EFC78117791408D1FB8B63A6492AD395AC2F360F24660022100AF78C624F5D107E9891C60BA637444F71A129E47135D36D92AFD39B856601A01");
+
+ {
+ auto const canonicality = ecdsaCanonicality(sig);
+ BEAST_EXPECT(canonicality);
+ BEAST_EXPECT(*canonicality == ECDSACanonicality::fullyCanonical);
+ }
+
+ {
+ auto const canonicality = ecdsaCanonicality(non);
+ BEAST_EXPECT(canonicality);
+ BEAST_EXPECT(*canonicality != ECDSACanonicality::fullyCanonical);
+ }
+
+ BEAST_EXPECT(verifyDigest(pk, digest, sig, false));
+ BEAST_EXPECT(verifyDigest(pk, digest, sig, true));
+ BEAST_EXPECT(verifyDigest(pk, digest, non, false));
+ BEAST_EXPECT(! verifyDigest(pk, digest, non, true));
+#endif
+ }
+
void testDigestSigning()
{
testcase ("secp256k1 digest");
@@ -279,6 +458,7 @@ public:
testBase58();
testDigestSigning();
testMiscOperations();
+ testCanonicality();
testcase ("secp256k1");
testSigning(KeyType::secp256k1);