From e05bf0844dd3d31eeb80c8328dcc72660b3e3143 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Thu, 26 Jan 2017 15:42:24 -0500 Subject: [PATCH] Changes for secp256k1 --- Builds/VisualStudio2015/RippleD.vcxproj | 16 ++ .../VisualStudio2015/RippleD.vcxproj.filters | 33 ++++ src/ripple/protocol/PublicKey.h | 6 + src/ripple/protocol/SecretKey.h | 9 + src/ripple/protocol/impl/PublicKey.cpp | 50 ++++- src/ripple/protocol/impl/SecretKey.cpp | 97 +++++++--- src/ripple/protocol/impl/secp256k1.h | 13 +- src/test/protocol/SecretKey_test.cpp | 180 ++++++++++++++++++ 8 files changed, 363 insertions(+), 41 deletions(-) diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj index c35972e969..863e8dceba 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 8cd6e8f9ed..d5d7e100e7 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 394723b136..e4ec472168 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 91e4210442..20f0e1c6bf 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 872b8c3942..11c09428f1 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 4ea7917c73..0a8dbca1e5 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 92870601bf..cc12d3d067 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 e3aa7460e3..7cc831396f 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);