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