From 6fccd07479061f806a392a2b41d56be84e1daf05 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Fri, 10 Jul 2015 10:03:08 -0700 Subject: [PATCH] Add new cryptographic token types: This replaces the equivalent functionality in RippleAddress. * New PublicKey, SecretKey, Seed, Generator * Removed AnyPublicKey, AnySecretKey --- Builds/VisualStudio2013/RippleD.vcxproj | 18 + .../VisualStudio2013/RippleD.vcxproj.filters | 18 + src/ripple/protocol/PublicKey.h | 232 +++++++++++ src/ripple/protocol/SecretKey.h | 165 ++++++++ src/ripple/protocol/impl/PublicKey.cpp | 365 ++++++++++++++++++ src/ripple/protocol/impl/SecretKey.cpp | 252 ++++++++++++ src/ripple/protocol/impl/secp256k1.h | 60 +++ src/ripple/protocol/tests/PublicKey_test.cpp | 231 +++++++++++ src/ripple/protocol/tokens.h | 4 + src/ripple/unity/protocol.cpp | 3 + 10 files changed, 1348 insertions(+) create mode 100644 src/ripple/protocol/PublicKey.h create mode 100644 src/ripple/protocol/SecretKey.h create mode 100644 src/ripple/protocol/impl/PublicKey.cpp create mode 100644 src/ripple/protocol/impl/SecretKey.cpp create mode 100644 src/ripple/protocol/impl/secp256k1.h create mode 100644 src/ripple/protocol/tests/PublicKey_test.cpp diff --git a/Builds/VisualStudio2013/RippleD.vcxproj b/Builds/VisualStudio2013/RippleD.vcxproj index 365becc708..4c59cff8eb 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj +++ b/Builds/VisualStudio2013/RippleD.vcxproj @@ -2773,6 +2773,10 @@ True True + + True + True + True True @@ -2781,6 +2785,12 @@ True True + + + + True + True + True True @@ -2887,6 +2897,8 @@ + + @@ -2897,6 +2909,8 @@ + + @@ -2951,6 +2965,10 @@ True True + + True + True + True True diff --git a/Builds/VisualStudio2013/RippleD.vcxproj.filters b/Builds/VisualStudio2013/RippleD.vcxproj.filters index b0ec0f015b..65c2f9f706 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2013/RippleD.vcxproj.filters @@ -3492,12 +3492,21 @@ ripple\protocol\impl + + ripple\protocol\impl + ripple\protocol\impl ripple\protocol\impl + + ripple\protocol\impl + + + ripple\protocol\impl + ripple\protocol\impl @@ -3591,6 +3600,9 @@ ripple\protocol + + ripple\protocol + ripple\protocol @@ -3606,6 +3618,9 @@ ripple\protocol + + ripple\protocol + ripple\protocol @@ -3678,6 +3693,9 @@ ripple\protocol\tests + + ripple\protocol\tests + ripple\protocol\tests diff --git a/src/ripple/protocol/PublicKey.h b/src/ripple/protocol/PublicKey.h new file mode 100644 index 0000000000..17881dc436 --- /dev/null +++ b/src/ripple/protocol/PublicKey.h @@ -0,0 +1,232 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 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_PROTOCOL_PUBLICKEY_H_INCLUDED +#define RIPPLE_PROTOCOL_PUBLICKEY_H_INCLUDED + +#include +#include // move to protocol/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ripple { + +/** A public key. + + Public keys are used in the public-key cryptography + system used to verify signatures attached to messages. + + The format of the public key is Ripple specific, + information needed to determine the cryptosystem + parameters used is stored inside the key. + + As of this writing two systems are supported: + + secp256k1 + ed25519 + + secp256k1 public keys consist of a 33 byte + compressed public key, with the lead byte equal + to 0x02 or 0x03. + + The ed25519 public keys consist of a 1 byte + prefix constant 0xED, followed by 32 bytes of + public key data. +*/ +class PublicKey +{ +protected: + std::size_t size_ = 0; + std::uint8_t buf_[33]; // should be large enough + +public: + PublicKey() = default; + PublicKey (PublicKey const& other); + PublicKey& operator= (PublicKey const& other); + + /** Create a public key. + + Preconditions: + + publicKeyType(Slice(data, size)) != boost::none + */ + explicit + PublicKey (Slice const& slice); + + KeyType + type() const; + + std::uint8_t const* + data() const noexcept + { + return buf_; + } + + std::size_t + size() const noexcept + { + return size_; + } + + Slice + slice() const noexcept + { + return { buf_, size_ }; + } + + bool + verify (Slice const& message, Slice const& sig, + bool mustBeFullyCanonical) const; +}; + +inline +bool +operator== (PublicKey const& lhs, + PublicKey const& rhs) +{ + return lhs.size() == rhs.size() && + std::memcmp(lhs.data(), + rhs.data(), rhs.size()) == 0; +} + +inline +bool +operator< (PublicKey const& lhs, + PublicKey const& rhs) +{ + return std::lexicographical_compare( + lhs.data(), lhs.data() + lhs.size(), + rhs.data(), rhs.data() + rhs.size()); +} + +template +void +hash_append (Hasher& h, + PublicKey const& pk) +{ + h(pk.data(), pk.size()); +} + +template<> +struct STExchange +{ + using value_type = PublicKey; + + static + void + get (boost::optional& t, + STBlob const& u) + { + t = boost::in_place(Slice(u.data(), u.size())); + } + + static + std::unique_ptr + set (SField const& f, PublicKey const& t) + { + return std::make_unique( + f, t.data(), t.size()); + } +}; + +//------------------------------------------------------------------------------ + +inline +std::string +toBase58 (TokenType type, PublicKey const& pk) +{ + return base58EncodeToken( + type, pk.data(), pk.size()); +} + +template<> +boost::optional +parseBase58 (TokenType type, std::string const& s); + +enum class ECDSACanonicality +{ + canonical, + fullyCanonical +}; + +/** Determines the canonicality of a signature. + + A canonical signature is in its most reduced form. + For example the R and S components do not contain + additional leading zeroes. However, even in + canonical form, (R,S) and (R,G-S) are both + valid signatures for message M. + + Therefore, to prevent malleability attacks we + define a fully canonical signature as one where: + + R < G - S + + where G is the curve order. + + This routine returns boost::none if the format + of the signature is invalid (for example, the + points are encoded incorrectly). + + @return boost::none if the signature fails + validity checks. + + @note Only the format of the signature is checked, + no verification cryptography is performed. +*/ +boost::optional +ecdsaCanonicality (Slice const& sig); + +/** Returns the type of public key. + + @return boost::none If the public key does + not represent a known type. +*/ +boost::optional +publicKeyType (Slice const& slice); + +/** Verify a signature. + + The algorithm is specific to Ripple: + secp256k1 signatures are computed + on the SHA512-Half of the message. +*/ +bool +verify (PublicKey const& pk, + Slice const& message, Slice const& signature); + +/** Calculate the 160-bit node ID from a node public key. */ +NodeID +calcNodeID (PublicKey const&); + +// VFALCO This belongs in AccountID.h but +// is here because of header issues +AccountID +calcAccountID (PublicKey const& pk); + +} // ripple + +#endif diff --git a/src/ripple/protocol/SecretKey.h b/src/ripple/protocol/SecretKey.h new file mode 100644 index 0000000000..2747065bc9 --- /dev/null +++ b/src/ripple/protocol/SecretKey.h @@ -0,0 +1,165 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 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_PROTOCOL_SECRETKEY_H_INCLUDED +#define RIPPLE_PROTOCOL_SECRETKEY_H_INCLUDED + +#include +#include +#include // move to protocol/ +#include +#include +#include + +namespace ripple { + +/** Seeds are used to generate deterministic secret keys. */ +class Seed +{ +private: + std::array buf_; + +public: + Seed() = default; + Seed (Seed const&) = default; + Seed& operator= (Seed const&) = default; + + /** Destroy the seed. + + The buffer will first be securely erased. + */ + ~Seed(); + + Seed (Slice const& slice); + + std::uint8_t const* + data() const + { + return buf_.data(); + } + + std::size_t + size() const + { + return buf_.size(); + } +}; + +//------------------------------------------------------------------------------ + +/** A secret key. */ +class SecretKey +{ +private: + std::uint8_t buf_[32]; + +public: + SecretKey() = default; + SecretKey (SecretKey const&) = default; + SecretKey& operator= (SecretKey const&) = default; + + ~SecretKey(); + + SecretKey (Slice const& slice); + + std::uint8_t const* + data() const + { + return buf_; + } + + std::size_t + size() const + { + return sizeof(buf_); + } +}; + +//------------------------------------------------------------------------------ + +/** Create a seed using secure random numbers. */ +Seed +randomSeed(); + +/** Generate a seed deterministically. + + The algorithm is specific to Ripple: + + The seed is calculated as the first 128 bits + of the SHA512-Half of the string text excluding + any terminating null. + + @note Unlike createSeedGeneric, this does not + attempt to interpret the string as hex + or other formats. +*/ +Seed +generateSeed (std::string const& passPhrase); + +/** Create a secret key using secure random numbers. */ +SecretKey +randomSecretKey(); + +/** Generate a new secret key deterministically. */ +SecretKey +generateSecretKey (Seed const& seed); + +/** Derive the public key from a secret key. */ +PublicKey +derivePublicKey (KeyType type, SecretKey const& sk); + +/** Generate a key pair deterministically. + + This algorithm is specific to Ripple: + + For secp256k1 key pairs, the seed is converted + to a Generator and used to compute the key pair + corresponding to ordinal 0 for the generator. +*/ +std::pair +generateKeyPair (KeyType type, Seed const& seed); + +/** Create a key pair using secure random numbers. */ +std::pair +randomKeyPair (KeyType type); + +/** Generate a signature for a message. + + The algorithm is specific to Ripple: + secp256k1 signatures are computed + on the SHA512-Half of the message. +*/ +/** @{ */ +Buffer +sign (PublicKey const& pk, + SecretKey const& sk, Slice const& message); + +inline +Buffer +sign (KeyType type, SecretKey const& sk, + Slice const& message) +{ + return sign (derivePublicKey(type, sk), + sk, message); +} +/** @} */ + +} // ripple + +#endif diff --git a/src/ripple/protocol/impl/PublicKey.cpp b/src/ripple/protocol/impl/PublicKey.cpp new file mode 100644 index 0000000000..b3929258bc --- /dev/null +++ b/src/ripple/protocol/impl/PublicKey.cpp @@ -0,0 +1,365 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 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 +#include +#include // + +namespace ripple { + +using uint264 = boost::multiprecision::number< + boost::multiprecision::cpp_int_backend< + 264, 264, boost::multiprecision::signed_magnitude, + boost::multiprecision::unchecked, void>>; + +template<> +boost::optional +parseBase58 (TokenType type, std::string const& s) +{ + auto const result = + decodeBase58Token(s, type); + if (result.empty()) + return boost::none; + if (result.size() != 33) + return boost::none; + return PublicKey(makeSlice(result)); +} + +//------------------------------------------------------------------------------ + +// Parse a length-prefixed number +// Format: 0x02 +static +boost::optional +sigPart (Slice& buf) +{ + if (buf.size() < 3 || buf[0] != 0x02) + return boost::none; + auto const len = buf[1]; + buf += 2; + if (len > buf.size() || len < 1 || len > 33) + return boost::none; + // Can't be negative + if ((buf[0] & 0x80) != 0) + return boost::none; + if (buf[0] == 0) + { + // Can't be zero + if (len == 1) + return boost::none; + // Can't be padded + if ((buf[1] & 0x80) == 0) + return boost::none; + } + boost::optional number = + Slice(buf.data(), len); + buf += len; + return number; +} + +template +void +swizzle (void* p); + +template<> +void +swizzle<4>(void* p) +{ + (*reinterpret_cast(p))= + beast::ByteOrder::swapIfLittleEndian( + *reinterpret_cast(p)); +} + +template<> +void +swizzle<8>(void* p) +{ + (*reinterpret_cast(p))= + beast::ByteOrder::swapIfLittleEndian( + *reinterpret_cast(p)); +} + +template +static +void +load (Number& mp, Slice const& buf) +{ + assert(buf.size() != 0); + auto& b = mp.backend(); // backend + auto const a = &b.limbs()[0]; // limb array + using Limb = std::decay_t< + decltype(a[0])>; // word type + b.resize((buf.size() + sizeof(Limb) - 1) / sizeof(Limb), 1); + std::memset(&a[0], 0, + b.size() * sizeof(Limb)); // zero fill + auto n = + buf.size() / sizeof(Limb); + auto s = reinterpret_cast( + buf.data() + buf.size() - sizeof(Limb)); + auto d = a; + while(n--) + { + *d = *s; + swizzle(d); + d++; + s--; + } + auto const r = + buf.size() % sizeof(Limb); + if (r > 0) + { + std::memcpy( + reinterpret_cast(d) + sizeof(Limb) - r, + buf.data(), r); + swizzle(d); + } +} + +static +std::string +sliceToHex (Slice const& slice) +{ + std::string s; + if (slice[0] & 0x80) + { + s.reserve(2 * (slice.size() + 2)); + s = "0x00"; + } + else + { + s.reserve(2 * (slice.size() + 1)); + s = "0x"; + } + for(int i = 0; i < slice.size(); ++i) + { + s += "0123456789ABCDEF"[((slice[i]&0xf0)>>4)]; + s += "0123456789ABCDEF"[((slice[i]&0x0f)>>0)]; + } + return s; +} + +/** Determine whether a signature is canonical. + Canonical signatures are important to protect against signature morphing + attacks. + @param vSig the signature data + @param sigLen the length of the signature + @param strict_param whether to enforce strictly canonical semantics + + @note For more details please see: + https://ripple.com/wiki/Transaction_Malleability + https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 + https://github.com/sipa/bitcoin/commit/58bc86e37fda1aec270bccb3df6c20fbd2a6591c +*/ +boost::optional +ecdsaCanonicality (Slice const& sig) +{ + static uint264 const G( + "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"); + + // The format of a signature should be: + // <30> [ <02> ] [ <02> ] + if ((sig.size() < 8) || (sig.size() > 72)) + return boost::none; + if ((sig[0] != 0x30) || (sig[1] != (sig.size() - 2))) + return boost::none; + Slice p = sig + 2; + auto r = sigPart(p); + auto s = sigPart(p); + if (! r || ! s || ! p.empty()) + return boost::none; +#if 0 + uint264 R; + uint264 S; + load(R, *r); + load(S, *s); +#else + uint264 R(sliceToHex(*r)); + uint264 S(sliceToHex(*s)); +#endif + + if (R >= G) + return boost::none; + if (S >= G) + return boost::none; + // (R,S) and (R,G-S) are canonical, + // but is fully canonical when S <= G-S + auto const Sp = G - S; + if (S > Sp) + return ECDSACanonicality::canonical; + return ECDSACanonicality::fullyCanonical; +} + +static +bool +ed25519Canonical (Slice const& sig) +{ + if (sig.size() != 64) + return false; + // Big-endian Order, the Ed25519 subgroup order + std::uint8_t const Order[] = + { + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0xDE, 0xF9, 0xDE, 0xA2, 0xF7, 0x9C, 0xD6, + 0x58, 0x12, 0x63, 0x1A, 0x5C, 0xF5, 0xD3, 0xED, + }; + // Take the second half of signature + // and byte-reverse it to big-endian. + auto const le = sig.data() + 32; + std::uint8_t S[32]; + std::reverse_copy(le, le + 32, S); + // Must be less than Order + return std::lexicographical_compare( + S, S + 32, Order, Order + 32); +} + +//------------------------------------------------------------------------------ + +PublicKey::PublicKey (Slice const& slice) +{ + if(! publicKeyType(slice)) + LogicError("PublicKey::PublicKey invalid type"); + size_ = slice.size(); + std::memcpy(buf_, slice.data(), slice.size()); +} + +PublicKey::PublicKey (PublicKey const& other) + : size_ (other.size_) +{ + std::memcpy(buf_, other.buf_, size_); +}; + +PublicKey& +PublicKey::operator=( + PublicKey const& other) +{ + size_ = other.size_; + std::memcpy(buf_, other.buf_, size_); + return *this; +} + +KeyType +PublicKey::type() const +{ + auto const result = + publicKeyType(Slice{ buf_, size_ }); + if (! result) + LogicError("PublicKey::type: invalid type"); + return *result; +} + +bool +PublicKey::verify (Slice const& m, + Slice const& sig, bool mustBeFullyCanonical) const +{ + switch(type()) + { + case KeyType::secp256k1: + { + auto const digest = sha512Half(m); + auto const canonicality = ecdsaCanonicality(sig); + if (! canonicality) + return false; + if (mustBeFullyCanonical && canonicality != + ECDSACanonicality::fullyCanonical) + return false; + return secp256k1_ecdsa_verify( + secp256k1Context(), secpp(digest.data()), + secpp(sig.data()), sig.size(), + secpp(buf_), size_) == 1; + } + default: + case KeyType::ed25519: + { + if (! ed25519Canonical(sig)) + return false; + return ed25519_sign_open( + m.data(), m.size(), buf_ + 1, + sig.data()) == 0; + } + } +} + +//------------------------------------------------------------------------------ + +boost::optional +publicKeyType (Slice const& slice) +{ + if (slice.size() == 33 && + slice[0] == 0xED) + return KeyType::ed25519; + if (slice.size() == 33 && + (slice[0] == 0x02 || + slice[0] == 0x03)) + return KeyType::secp256k1; + return boost::none; +} + +bool +verify (PublicKey const& pk, + Slice const& m, Slice const& sig) +{ + switch(pk.type()) + { + default: + case KeyType::secp256k1: + { + sha512_half_hasher h; + h(m.data(), m.size()); + auto const digest = + sha512_half_hasher::result_type(h); + return secp256k1_ecdsa_verify( + secp256k1Context(), digest.data(), + sig.data(), sig.size(), + pk.data(), pk.size()) == 1; + } + case KeyType::ed25519: + { + if (sig.size() != 64) + return false; + return ed25519_sign_open(m.data(), + m.size(), pk.data(), sig.data()) == 0; + } + } +} + +NodeID +calcNodeID (PublicKey const& pk) +{ + ripesha_hasher h; + h(pk.data(), pk.size()); + auto const digest = static_cast< + ripesha_hasher::result_type>(h); + static_assert(NodeID::bytes == + sizeof(ripesha_hasher::result_type), ""); + NodeID result; + std::memcpy(result.data(), + digest.data(), digest.size()); + return result; +} + +} // ripple + diff --git a/src/ripple/protocol/impl/SecretKey.cpp b/src/ripple/protocol/impl/SecretKey.cpp new file mode 100644 index 0000000000..dfc4047163 --- /dev/null +++ b/src/ripple/protocol/impl/SecretKey.cpp @@ -0,0 +1,252 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 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 +#include +#include + +namespace ripple { + +Seed::~Seed() +{ + beast::secure_erase( + buf_.data(), buf_.size()); +} + +Seed::Seed (Slice const& slice) +{ + if (slice.size() != buf_.size()) + LogicError("Seed::Seed: invalid size"); + std::memcpy(buf_.data(), + slice.data(), buf_.size()); +} + +//------------------------------------------------------------------------------ + +SecretKey::~SecretKey() +{ + beast::secure_erase(buf_, sizeof(buf_)); +} + +SecretKey::SecretKey (Slice const& slice) +{ + if (slice.size() != sizeof(buf_)) + LogicError("SecretKey::SecretKey: invalid size"); + std::memcpy(buf_, slice.data(), sizeof(buf_)); +} + +//------------------------------------------------------------------------------ + +/** Produces a sequence of secp256k1 key pairs. */ +class Generator +{ +private: + Blob gen_; // VFALCO compile time size? + +public: + explicit + Generator (Seed const& seed) + { + uint128 ui; + std::memcpy(ui.data(), + seed.data(), seed.size()); + gen_ = generateRootDeterministicPublicKey(ui); + } + + /** Generate the nth key pair. + + The seed is required to produce the private key. + */ + std::pair + operator()(Seed const& seed, std::size_t ordinal) const + { + uint128 ui; + std::memcpy(ui.data(), seed.data(), seed.size()); + auto gsk = generatePrivateDeterministicKey(gen_, ui, ordinal); + auto gpk = generatePublicDeterministicKey(gen_, ordinal); + SecretKey const sk(Slice{ gsk.data(), gsk.size() }); + PublicKey const pk(Slice{ gpk.data(), gpk.size() }); + beast::secure_erase(ui.data(), ui.size()); + beast::secure_erase(gsk.data(), gsk.size()); + return { pk, sk }; + } + + /** Generate the nth public key. */ + PublicKey + operator()(std::size_t ordinal) const + { + auto gpk = generatePublicDeterministicKey(gen_, ordinal); + return PublicKey(Slice{ gpk.data(), gpk.size() }); + } +}; + +//------------------------------------------------------------------------------ + +Buffer +sign (PublicKey const& pk, + SecretKey const& sk, Slice const& m) +{ + auto const type = + publicKeyType(pk.slice()); + if (! type) + LogicError("sign: invalid type"); + switch(*type) + { + case KeyType::ed25519: + { + auto const pk = derivePublicKey( + KeyType::ed25519, sk); + Buffer b(64); + ed25519_sign(m.data(), m.size(), + sk.data(), pk.data() + 1, b.data()); + return b; + } + default: + // VFALCO Work-around for missing msvc [[noreturn]] + LogicError("sign: invalid type"); + case KeyType::secp256k1: + { + sha512_half_hasher h; + 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) + LogicError("sign: secp256k1_ecdsa_sign failed"); + return Buffer(sig, siglen); + } + } +} + +Seed +randomSeed() +{ + std::uint8_t buf[16]; + random_fill(buf, sizeof(buf)); + Seed seed(Slice{ buf, sizeof(buf) }); + beast::secure_erase(buf, sizeof(buf)); + return seed; +} + +Seed +generateSeed (std::string const& passPhrase) +{ + sha512_half_hasher_s h; + h(passPhrase.data(), passPhrase.size()); + auto const digest = + sha512_half_hasher::result_type(h); + return Seed({ digest.data(), 16 }); +} + +SecretKey +randomSecretKey() +{ + std::uint8_t buf[32]; + random_fill(buf, sizeof(buf)); + SecretKey sk(Slice{ buf, sizeof(buf) }); + beast::secure_erase(buf, sizeof(buf)); + return sk; +} + +// VFALCO TODO Rewrite all this without using OpenSSL +// or calling into GenerateDetermisticKey + +SecretKey +generateSecretKey (Seed const& seed) +{ + uint128 ps; + std::memcpy(ps.data(), + seed.data(), seed.size()); + auto const upk = + generateRootDeterministicPrivateKey(ps); + return SecretKey(Slice{ upk.data(), upk.size() }); +} + +PublicKey +derivePublicKey (KeyType type, SecretKey const& sk) +{ + switch(type) + { + case KeyType::secp256k1: + { + int len; + unsigned char buf[33]; + auto const result = + secp256k1_ec_pubkey_create( + secp256k1Context(), + buf, &len, sk.data(), 1); + if (result != 1) + LogicError("derivePublicKey: failure"); + return PublicKey(Slice{ buf, + static_cast(len) }); + } + default: + // VFALCO Work-around for missing msvc [[noreturn]] + LogicError("derivePublicKey: bad key type"); + case KeyType::ed25519: + { + unsigned char buf[33]; + buf[0] = 0xED; + ed25519_publickey(sk.data(), &buf[1]); + return PublicKey(Slice{ buf, sizeof(buf) }); + } + }; +} + +std::pair +generateKeyPair (KeyType type, Seed const& seed) +{ + switch(type) + { + case KeyType::secp256k1: + { + Generator g(seed); + return g(seed, 0); + } + default: + case KeyType::ed25519: + { + auto const sk = generateSecretKey(seed); + return { derivePublicKey(type, sk), sk }; + } + } +} + +std::pair +randomKeyPair (KeyType type) +{ + auto const sk = randomSecretKey(); + return { derivePublicKey(type, sk), sk }; +} + +} // ripple + diff --git a/src/ripple/protocol/impl/secp256k1.h b/src/ripple/protocol/impl/secp256k1.h new file mode 100644 index 0000000000..7924de363c --- /dev/null +++ b/src/ripple/protocol/impl/secp256k1.h @@ -0,0 +1,60 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 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_PROTOCOL_SECP256K1_H_INCLUDED +#define RIPPLE_PROTOCOL_SECP256K1_H_INCLUDED + +#include +#include + +namespace ripple { + +template +secp256k1_context_t const* +secp256k1Context() +{ + struct holder + { + secp256k1_context_t* impl; + holder() + : impl (secp256k1_context_create( + SECP256K1_CONTEXT_VERIFY + + SECP256K1_CONTEXT_SIGN)) + { + } + + ~holder() + { + secp256k1_context_destroy(impl); + } + }; + static beast::static_initializer const h; + return h->impl; +} + +inline +unsigned char const* +secpp(void const* p) +{ + return static_cast(p); +} + +} // ripple + +#endif diff --git a/src/ripple/protocol/tests/PublicKey_test.cpp b/src/ripple/protocol/tests/PublicKey_test.cpp new file mode 100644 index 0000000000..99bf56ff86 --- /dev/null +++ b/src/ripple/protocol/tests/PublicKey_test.cpp @@ -0,0 +1,231 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 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 + +namespace ripple { + +class PublicKey_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); + } + } + + blob + sig (std::string const& hex) + { + blob b; + hex_to_binary (hex.begin (), hex.end (), b); + return b; + } + + bool + check (boost::optional answer, + std::string const& s) + { + return ecdsaCanonicality(makeSlice(sig(s))) == + answer; + } + + void run() override + { + // Fully canonical + expect (check(ECDSACanonicality::fullyCanonical, + "3045" + "022100FF478110D1D4294471EC76E0157540C2181F47DEBD25D7F9E7DDCCCD47EEE905" + "0220078F07CDAE6C240855D084AD91D1479609533C147C93B0AEF19BC9724D003F28")); + expect (check(ECDSACanonicality::fullyCanonical, + "3045" + "0221009218248292F1762D8A51BE80F8A7F2CD288D810CE781D5955700DA1684DF1D2D" + "022041A1EE1746BFD72C9760CC93A7AAA8047D52C8833A03A20EAAE92EA19717B454")); + expect (check(ECDSACanonicality::fullyCanonical, + "3044" + "02206A9E43775F73B6D1EC420E4DDD222A80D4C6DF5D1BEECC431A91B63C928B7581" + "022023E9CC2D61DDA6F73EAA6BCB12688BEB0F434769276B3127E4044ED895C9D96B")); + expect (check(ECDSACanonicality::fullyCanonical, + "3044" + "022056E720007221F3CD4EFBB6352741D8E5A0968D48D8D032C2FBC4F6304AD1D04E" + "02201F39EB392C20D7801C3E8D81D487E742FA84A1665E923225BD6323847C71879F")); + expect (check(ECDSACanonicality::fullyCanonical, + "3045" + "022100FDFD5AD05518CEA0017A2DCB5C4DF61E7C73B6D3A38E7AE93210A1564E8C2F12" + "0220214FF061CCC123C81D0BB9D0EDEA04CD40D96BF1425D311DA62A7096BB18EA18")); + + // Canonical but not fully canonical + expect (check(ECDSACanonicality::canonical, + "3046" + "022100F477B3FA6F31C7CB3A0D1AD94A231FDD24B8D78862EE334CEA7CD08F6CBC0A1B" + "022100928E6BCF1ED2684679730C5414AEC48FD62282B090041C41453C1D064AF597A1")); + expect (check(ECDSACanonicality::canonical, + "3045" + "022063E7C7CA93CB2400E413A342C027D00665F8BAB9C22EF0A7B8AE3AAF092230B6" + "0221008F2E8BB7D09521ABBC277717B14B93170AE6465C5A1B36561099319C4BEB254C")); + expect (check(ECDSACanonicality::canonical, + "3046" + "02210099DCA1188663DDEA506A06A7B20C2B7D8C26AFF41DECE69D6C5F7C967D32625F" + "022100897658A6B1F9EEE5D140D7A332DA0BD73BB98974EA53F6201B01C1B594F286EA")); + expect (check(ECDSACanonicality::canonical, + "3045" + "02200855DE366E4E323AA2CE2A25674401A7D11F72EC432770D07F7B57DF7387AEC0" + "022100DA4C6ADDEA14888858DE2AC5B91ED9050D6972BB388DEF582628CEE32869AE35")); + + // valid + expect (check(ECDSACanonicality::fullyCanonical, + "3006" + "020101" + "020102")); + expect (check(ECDSACanonicality::fullyCanonical, + "3044" + "02203932c892e2e550f3af8ee4ce9c215a87f9bb831dcac87b2838e2c2eaa891df0c" + "022030b61dd36543125d56b9f9f3a1f53189e5af33cdda8d77a5209aec03978fa001")); + expect (check(ECDSACanonicality::canonical, + "3045" + "0220076045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40f90a" + "0221008fffd599910eefe00bc803c688eca1d2ba7f6b180620eaa03488e6585db6ba01")); + expect (check(ECDSACanonicality::canonical, + "3046" + "022100876045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40f90a" + "0221008fffd599910eefe00bc803c688c2eca1d2ba7f6b180620eaa03488e6585db6ba")); + + expect (check(boost::none, + "3005" + "0201FF" + "0200")); + expect (check(boost::none, + "3006" + "020101" + "020202")); + expect (check(boost::none, + "3006" + "020701" + "020102")); + expect (check(boost::none, + "3006" + "020401" + "020102")); + expect (check(boost::none, + "3006" + "020501" + "020102")); + expect (check(boost::none, + "3006" + "020201" + "020102")); + expect (check(boost::none, + "3006" + "020301" + "020202")); + expect (check(boost::none, + "3006" + "020401" + "020202")); + expect (check(boost::none, + "3047" + "0221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105" + "022200002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed")); + expect (check(boost::none, + "3144" + "02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105" + "02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed")); + expect (check(boost::none, + "3045" + "02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105" + "02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed")); + expect (check(boost::none, + "301F" + "01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1")); + expect (check(boost::none, + "3045" + "02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105" + "02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed00")); + expect (check(boost::none, + "3044" + "01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105" + "02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed")); + expect (check(boost::none, + "3024" + "0200" + "02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed")); + expect (check(boost::none, + "3044" + "02208990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105" + "02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed")); + expect (check(boost::none, + "3045" + "0221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105" + "02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed")); + expect (check(boost::none, + "3044" + "02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105012" + "02d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed")); + expect (check(boost::none, + "3024" + "02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105" + "0200")); + expect (check(boost::none, + "3044" + "02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105" + "0220fd5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed")); + expect (check(boost::none, + "3045" + "02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105" + "0221002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed")); + } +}; + +BEAST_DEFINE_TESTSUITE(PublicKey,protocol,ripple); + +} // ripple diff --git a/src/ripple/protocol/tokens.h b/src/ripple/protocol/tokens.h index 5524dc1d8e..f8585d90db 100644 --- a/src/ripple/protocol/tokens.h +++ b/src/ripple/protocol/tokens.h @@ -42,6 +42,10 @@ template boost::optional parseBase58 (std::string const& s); +template +boost::optional +parseBase58 (TokenType type, std::string const& s); + template boost::optional parseHex (std::string const& s); diff --git a/src/ripple/unity/protocol.cpp b/src/ripple/unity/protocol.cpp index 93209ce2f1..9c41985b03 100644 --- a/src/ripple/unity/protocol.cpp +++ b/src/ripple/unity/protocol.cpp @@ -30,8 +30,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -61,6 +63,7 @@ #include #include #include +#include #include #include #include