rippled
Handshake.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/app/ledger/LedgerMaster.h>
21 #include <ripple/app/main/Application.h>
22 #include <ripple/basics/base64.h>
23 #include <ripple/basics/safe_cast.h>
24 #include <ripple/beast/core/LexicalCast.h>
25 #include <ripple/beast/rfc2616.h>
26 #include <ripple/overlay/impl/Handshake.h>
27 #include <ripple/protocol/digest.h>
28 #include <boost/regex.hpp>
29 #include <algorithm>
30 #include <chrono>
31 
32 // VFALCO Shouldn't we have to include the OpenSSL
33 // headers or something for SSL_get_finished?
34 
35 namespace ripple {
36 
51 static boost::optional<base_uint<512>>
52 hashLastMessage(SSL const* ssl, size_t (*get)(const SSL*, void*, size_t))
53 {
54  constexpr std::size_t sslMinimumFinishedLength = 12;
55 
56  unsigned char buf[1024];
57  size_t len = get(ssl, buf, sizeof(buf));
58 
59  if (len < sslMinimumFinishedLength)
60  return boost::none;
61 
62  sha512_hasher h;
63 
64  base_uint<512> cookie;
65  SHA512(buf, len, cookie.data());
66  return cookie;
67 }
68 
69 boost::optional<uint256>
71 {
72  auto const cookie1 = hashLastMessage(ssl.native_handle(), SSL_get_finished);
73  if (!cookie1)
74  {
75  JLOG(journal.error()) << "Cookie generation: local setup not complete";
76  return boost::none;
77  }
78 
79  auto const cookie2 =
80  hashLastMessage(ssl.native_handle(), SSL_get_peer_finished);
81  if (!cookie2)
82  {
83  JLOG(journal.error()) << "Cookie generation: peer setup not complete";
84  return boost::none;
85  }
86 
87  auto const result = (*cookie1 ^ *cookie2);
88 
89  // Both messages hash to the same value and the cookie
90  // is 0. Don't allow this.
91  if (result == beast::zero)
92  {
93  JLOG(journal.error())
94  << "Cookie generation: identical finished messages";
95  return boost::none;
96  }
97 
98  return sha512Half(Slice(result.data(), result.size()));
99 }
100 
101 void
103  boost::beast::http::fields& h,
104  ripple::uint256 const& sharedValue,
105  boost::optional<std::uint32_t> networkID,
106  beast::IP::Address public_ip,
107  beast::IP::Address remote_ip,
108  Application& app)
109 {
110  if (networkID)
111  {
112  // The network identifier, if configured, can be used to specify
113  // what network we intend to connect to and detect if the remote
114  // end connects to the same network.
115  h.insert("Network-ID", std::to_string(*networkID));
116  }
117 
118  h.insert(
119  "Network-Time",
120  std::to_string(app.timeKeeper().now().time_since_epoch().count()));
121 
122  h.insert(
123  "Public-Key",
125 
126  {
127  auto const sig = signDigest(
128  app.nodeIdentity().first, app.nodeIdentity().second, sharedValue);
129  h.insert("Session-Signature", base64_encode(sig.data(), sig.size()));
130  }
131 
132  if (!app.config().SERVER_DOMAIN.empty())
133  h.insert("Server-Domain", app.config().SERVER_DOMAIN);
134 
135  if (beast::IP::is_public(remote_ip))
136  h.insert("Remote-IP", remote_ip.to_string());
137 
138  if (!public_ip.is_unspecified())
139  h.insert("Local-IP", public_ip.to_string());
140 
141  if (auto const cl = app.getLedgerMaster().getClosedLedger())
142  {
143  // TODO: Use hex for these
144  h.insert(
145  "Closed-Ledger",
146  base64_encode(cl->info().hash.begin(), cl->info().hash.size()));
147  h.insert(
148  "Previous-Ledger",
150  cl->info().parentHash.begin(), cl->info().parentHash.size()));
151  }
152 }
153 
154 PublicKey
156  boost::beast::http::fields const& headers,
157  ripple::uint256 const& sharedValue,
158  boost::optional<std::uint32_t> networkID,
159  beast::IP::Address public_ip,
160  beast::IP::Address remote,
161  Application& app)
162 {
163  if (auto const iter = headers.find("Server-Domain"); iter != headers.end())
164  {
165  if (!isProperlyFormedTomlDomain(iter->value().to_string()))
166  throw std::runtime_error("Invalid server domain");
167  }
168 
169  if (auto const iter = headers.find("Network-ID"); iter != headers.end())
170  {
171  std::uint32_t nid;
172 
173  if (!beast::lexicalCastChecked(nid, iter->value().to_string()))
174  throw std::runtime_error("Invalid peer network identifier");
175 
176  if (networkID && nid != *networkID)
177  throw std::runtime_error("Peer is on a different network");
178  }
179 
180  if (auto const iter = headers.find("Network-Time"); iter != headers.end())
181  {
182  auto const netTime =
183  [str = iter->value().to_string()]() -> TimeKeeper::time_point {
184  TimeKeeper::duration::rep val;
185 
186  if (beast::lexicalCastChecked(val, str))
188 
189  // It's not an error for the header field to not be present but if
190  // it is present and it contains junk data, that is an error.
191  throw std::runtime_error("Invalid peer clock timestamp");
192  }();
193 
194  using namespace std::chrono;
195 
196  auto const ourTime = app.timeKeeper().now();
197  auto const tolerance = 20s;
198 
199  // We can't blindly "return a-b;" because TimeKeeper::time_point
200  // uses an unsigned integer for representing durations, which is
201  // a problem when trying to subtract time points.
202  // FIXME: @HowardHinnant, should we migrate to using std::int64_t?
203  auto calculateOffset = [](TimeKeeper::time_point a,
205  if (a > b)
206  return duration_cast<std::chrono::seconds>(a - b);
207  return -duration_cast<std::chrono::seconds>(b - a);
208  };
209 
210  auto const offset = calculateOffset(netTime, ourTime);
211 
212  if (date::abs(offset) > tolerance)
213  throw std::runtime_error("Peer clock is too far off");
214  }
215 
216  PublicKey const publicKey = [&headers] {
217  if (auto const iter = headers.find("Public-Key"); iter != headers.end())
218  {
219  auto pk = parseBase58<PublicKey>(
220  TokenType::NodePublic, iter->value().to_string());
221 
222  if (pk)
223  {
224  if (publicKeyType(*pk) != KeyType::secp256k1)
225  throw std::runtime_error("Unsupported public key type");
226 
227  return *pk;
228  }
229  }
230 
231  throw std::runtime_error("Bad node public key");
232  }();
233 
234  if (publicKey == app.nodeIdentity().first)
235  throw std::runtime_error("Self connection");
236 
237  // This check gets two birds with one stone:
238  //
239  // 1) it verifies that the node we are talking to has access to the
240  // private key corresponding to the public node identity it claims.
241  // 2) it verifies that our SSL session is end-to-end with that node
242  // and not through a proxy that establishes two separate sessions.
243  {
244  auto const iter = headers.find("Session-Signature");
245 
246  if (iter == headers.end())
247  throw std::runtime_error("No session signature specified");
248 
249  auto sig = base64_decode(iter->value().to_string());
250 
251  if (!verifyDigest(publicKey, sharedValue, makeSlice(sig), false))
252  throw std::runtime_error("Failed to verify session");
253  }
254 
255  if (auto const iter = headers.find("Local-IP"); iter != headers.end())
256  {
257  boost::system::error_code ec;
258  auto const local_ip = boost::asio::ip::address::from_string(
259  iter->value().to_string(), ec);
260 
261  if (ec)
262  throw std::runtime_error("Invalid Local-IP");
263 
264  if (beast::IP::is_public(remote) && remote != local_ip)
265  throw std::runtime_error(
266  "Incorrect Local-IP: " + remote.to_string() + " instead of " +
267  local_ip.to_string());
268  }
269 
270  if (auto const iter = headers.find("Remote-IP"); iter != headers.end())
271  {
272  boost::system::error_code ec;
273  auto const remote_ip = boost::asio::ip::address::from_string(
274  iter->value().to_string(), ec);
275 
276  if (ec)
277  throw std::runtime_error("Invalid Remote-IP");
278 
279  if (beast::IP::is_public(remote) &&
280  !beast::IP::is_unspecified(public_ip))
281  {
282  // We know our public IP and peer reports our connection came
283  // from some other IP.
284  if (remote_ip != public_ip)
285  throw std::runtime_error(
286  "Incorrect Remote-IP: " + public_ip.to_string() +
287  " instead of " + remote_ip.to_string());
288  }
289  }
290 
291  return publicKey;
292 }
293 
294 } // namespace ripple
ripple::Application
Definition: Application.h:97
ripple::makeSlice
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition: Slice.h:240
ripple::publicKeyType
boost::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
Definition: PublicKey.cpp:203
ripple::Slice
An immutable linear range of bytes.
Definition: Slice.h:44
ripple::openssl_sha512_hasher
SHA-512 digest.
Definition: digest.h:68
ripple::base64_encode
std::string base64_encode(std::uint8_t const *data, std::size_t len)
Definition: base64.cpp:236
ripple::toBase58
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:29
ripple::Application::timeKeeper
virtual TimeKeeper & timeKeeper()=0
ripple::buildHandshake
void buildHandshake(boost::beast::http::fields &h, ripple::uint256 const &sharedValue, boost::optional< std::uint32_t > networkID, beast::IP::Address public_ip, beast::IP::Address remote_ip, Application &app)
Insert fields headers necessary for upgrading the link to the peer protocol.
Definition: Handshake.cpp:102
ripple::base_uint::data
pointer data()
Definition: base_uint.h:113
algorithm
ripple::hashLastMessage
static boost::optional< base_uint< 512 > > hashLastMessage(SSL const *ssl, size_t(*get)(const SSL *, void *, size_t))
Hashes the latest finished message from an SSL stream.
Definition: Handshake.cpp:52
ripple::verifyHandshake
PublicKey verifyHandshake(boost::beast::http::fields const &headers, ripple::uint256 const &sharedValue, boost::optional< std::uint32_t > networkID, beast::IP::Address public_ip, beast::IP::Address remote, Application &app)
Validate header fields necessary for upgrading the link to the peer protocol.
Definition: Handshake.cpp:155
ripple::base_uint
Integers of any length that is a multiple of 32-bits.
Definition: base_uint.h:73
beast::IP::Address
boost::asio::ip::address Address
Definition: IPAddress.h:41
ripple::Application::getLedgerMaster
virtual LedgerMaster & getLedgerMaster()=0
ripple::PublicKey
A public key.
Definition: PublicKey.h:59
ripple::signDigest
Buffer signDigest(PublicKey const &pk, SecretKey const &sk, uint256 const &digest)
Generate a signature for a message digest.
Definition: SecretKey.cpp:98
chrono
ripple::Application::config
virtual Config & config()=0
ripple::Application::nodeIdentity
virtual std::pair< PublicKey, SecretKey > const & nodeIdentity()=0
std::to_string
T to_string(T... args)
ripple::makeSharedValue
boost::optional< uint256 > makeSharedValue(stream_type &ssl, beast::Journal journal)
Computes a shared value based on the SSL connection state.
Definition: Handshake.cpp:70
ripple::base64_decode
std::string base64_decode(std::string const &data)
Definition: base64.cpp:245
beast::Journal::error
Stream error() const
Definition: Journal.h:333
std::runtime_error
STL class.
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
std::uint32_t
beast::IP::is_public
bool is_public(AddressV4 const &addr)
Returns true if the address is a public routable address.
Definition: IPAddressV4.cpp:41
ripple::verifyDigest
bool verifyDigest(PublicKey const &publicKey, uint256 const &digest, Slice const &sig, bool mustBeFullyCanonical)
Verify a secp256k1 signature on the digest of a message.
Definition: PublicKey.cpp:218
ripple::stream_type
boost::beast::ssl_stream< socket_type > stream_type
Definition: Handshake.h:40
ripple::KeyType::secp256k1
@ secp256k1
ripple::LedgerMaster::getClosedLedger
std::shared_ptr< Ledger const > getClosedLedger()
Definition: LedgerMaster.h:87
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::Config::SERVER_DOMAIN
std::string SERVER_DOMAIN
Definition: Config.h:204
beast::lexicalCastChecked
bool lexicalCastChecked(Out &out, In in)
Intelligently convert from one type to another.
Definition: LexicalCast.h:266
ripple::sha512Half
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition: digest.h:216
ripple::TimeKeeper::now
virtual time_point now() const override=0
Returns the estimate of wall time, in network time.
std::string::empty
T empty(T... args)
ripple::TokenType::NodePublic
@ NodePublic
std::size_t
ripple::isProperlyFormedTomlDomain
bool isProperlyFormedTomlDomain(std::string const &domain)
Determines if the given string looks like a TOML-file hosting domain.
beast::IP::is_unspecified
bool is_unspecified(Address const &addr)
Returns true if the address is unspecified.
Definition: IPAddress.h:59
beast::abstract_clock< NetClock >::time_point
typename NetClock ::time_point time_point
Definition: abstract_clock.h:63
beast::abstract_clock< NetClock >::duration
typename NetClock ::duration duration
Definition: abstract_clock.h:62
ripple::get
T & get(EitherAmount &amt)
Definition: AmountSpec.h:116
std::chrono