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 (beast::IP::is_public(remote_ip))
133  h.insert("Remote-IP", remote_ip.to_string());
134 
135  if (!public_ip.is_unspecified())
136  h.insert("Local-IP", public_ip.to_string());
137 
138  if (auto const cl = app.getLedgerMaster().getClosedLedger())
139  {
140  // TODO: Use hex for these
141  h.insert(
142  "Closed-Ledger",
143  base64_encode(cl->info().hash.begin(), cl->info().hash.size()));
144  h.insert(
145  "Previous-Ledger",
147  cl->info().parentHash.begin(), cl->info().parentHash.size()));
148  }
149 }
150 
151 PublicKey
153  boost::beast::http::fields const& headers,
154  ripple::uint256 const& sharedValue,
155  boost::optional<std::uint32_t> networkID,
156  beast::IP::Address public_ip,
157  beast::IP::Address remote,
158  Application& app)
159 {
160  if (networkID)
161  {
162  if (auto const iter = headers.find("Network-ID"); iter != headers.end())
163  {
164  std::uint32_t nid;
165 
166  if (!beast::lexicalCastChecked(nid, iter->value().to_string()))
167  throw std::runtime_error("Invalid peer network identifier");
168 
169  if (nid != *networkID)
170  throw std::runtime_error("Peer is on a different network");
171  }
172  }
173 
174  if (auto const iter = headers.find("Network-Time"); iter != headers.end())
175  {
176  auto const netTime =
177  [str = iter->value().to_string()]() -> TimeKeeper::time_point {
178  TimeKeeper::duration::rep val;
179 
180  if (beast::lexicalCastChecked(val, str))
182 
183  // It's not an error for the header field to not be present but if
184  // it is present and it contains junk data, that is an error.
185  throw std::runtime_error("Invalid peer clock timestamp");
186  }();
187 
188  using namespace std::chrono;
189 
190  auto const ourTime = app.timeKeeper().now();
191  auto const tolerance = 20s;
192 
193  // We can't blindly "return a-b;" because TimeKeeper::time_point
194  // uses an unsigned integer for representing durations, which is
195  // a problem when trying to subtract time points.
196  // FIXME: @HowardHinnant, should we migrate to using std::int64_t?
197  auto calculateOffset = [](TimeKeeper::time_point a,
199  if (a > b)
200  return duration_cast<std::chrono::seconds>(a - b);
201  return -duration_cast<std::chrono::seconds>(b - a);
202  };
203 
204  auto const offset = calculateOffset(netTime, ourTime);
205 
206  if (date::abs(offset) > tolerance)
207  throw std::runtime_error("Peer clock is too far off");
208  }
209 
210  PublicKey const publicKey = [&headers] {
211  if (auto const iter = headers.find("Public-Key"); iter != headers.end())
212  {
213  auto pk = parseBase58<PublicKey>(
214  TokenType::NodePublic, iter->value().to_string());
215 
216  if (pk)
217  {
218  if (publicKeyType(*pk) != KeyType::secp256k1)
219  throw std::runtime_error("Unsupported public key type");
220 
221  return *pk;
222  }
223  }
224 
225  throw std::runtime_error("Bad node public key");
226  }();
227 
228  if (publicKey == app.nodeIdentity().first)
229  throw std::runtime_error("Self connection");
230 
231  // This check gets two birds with one stone:
232  //
233  // 1) it verifies that the node we are talking to has access to the
234  // private key corresponding to the public node identity it claims.
235  // 2) it verifies that our SSL session is end-to-end with that node
236  // and not through a proxy that establishes two separate sessions.
237  {
238  auto const iter = headers.find("Session-Signature");
239 
240  if (iter == headers.end())
241  throw std::runtime_error("No session signature specified");
242 
243  auto sig = base64_decode(iter->value().to_string());
244 
245  if (!verifyDigest(publicKey, sharedValue, makeSlice(sig), false))
246  throw std::runtime_error("Failed to verify session");
247  }
248 
249  if (auto const iter = headers.find("Local-IP"); iter != headers.end())
250  {
251  boost::system::error_code ec;
252  auto const local_ip = boost::asio::ip::address::from_string(
253  iter->value().to_string(), ec);
254 
255  if (ec)
256  throw std::runtime_error("Invalid Local-IP");
257 
258  if (beast::IP::is_public(remote) && remote != local_ip)
259  throw std::runtime_error(
260  "Incorrect Local-IP: " + remote.to_string() + " instead of " +
261  local_ip.to_string());
262  }
263 
264  if (auto const iter = headers.find("Remote-IP"); iter != headers.end())
265  {
266  boost::system::error_code ec;
267  auto const remote_ip = boost::asio::ip::address::from_string(
268  iter->value().to_string(), ec);
269 
270  if (ec)
271  throw std::runtime_error("Invalid Remote-IP");
272 
273  if (beast::IP::is_public(remote) &&
274  !beast::IP::is_unspecified(public_ip))
275  {
276  // We know our public IP and peer reports our connection came
277  // from some other IP.
278  if (remote_ip != public_ip)
279  throw std::runtime_error(
280  "Incorrect Remote-IP: " + public_ip.to_string() +
281  " instead of " + remote_ip.to_string());
282  }
283  }
284 
285  return publicKey;
286 }
287 
288 } // 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:69
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:103
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:152
ripple::base_uint
Definition: base_uint.h:63
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::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
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:227
ripple::TimeKeeper::now
virtual time_point now() const override=0
Returns the estimate of wall time, in network time.
ripple::TokenType::NodePublic
@ NodePublic
std::size_t
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