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/overlay/impl/Handshake.h>
21 #include <ripple/app/ledger/LedgerMaster.h>
22 #include <ripple/app/main/Application.h>
23 #include <ripple/basics/base64.h>
24 #include <ripple/basics/safe_cast.h>
25 #include <ripple/beast/rfc2616.h>
26 #include <ripple/beast/core/LexicalCast.h>
27 #include <ripple/protocol/digest.h>
28 #include <boost/regex.hpp>
29 #include <algorithm>
30 #include <chrono>
31 
32 
33 // VFALCO Shouldn't we have to include the OpenSSL
34 // headers or something for SSL_get_finished?
35 
36 namespace ripple {
37 
52 static
53 boost::optional<base_uint<512>>
54 hashLastMessage (SSL const* ssl,
55  size_t (*get)(const SSL *, void *, size_t))
56 {
57  constexpr std::size_t sslMinimumFinishedLength = 12;
58 
59  unsigned char buf[1024];
60  size_t len = get(ssl, buf, sizeof(buf));
61 
62  if(len < sslMinimumFinishedLength)
63  return boost::none;
64 
65  sha512_hasher h;
66 
67  base_uint<512> cookie;
68  SHA512 (buf, len, cookie.data());
69  return cookie;
70 }
71 
72 boost::optional<uint256>
74 {
75  auto const cookie1 = hashLastMessage(
76  ssl.native_handle(), SSL_get_finished);
77  if (!cookie1)
78  {
79  JLOG (journal.error()) << "Cookie generation: local setup not complete";
80  return boost::none;
81  }
82 
83  auto const cookie2 = hashLastMessage(
84  ssl.native_handle(), SSL_get_peer_finished);
85  if (!cookie2)
86  {
87  JLOG (journal.error()) << "Cookie generation: peer setup not complete";
88  return boost::none;
89  }
90 
91  auto const result = (*cookie1 ^ *cookie2);
92 
93  // Both messages hash to the same value and the cookie
94  // is 0. Don't allow this.
95  if (result == beast::zero)
96  {
97  JLOG(journal.error()) << "Cookie generation: identical finished messages";
98  return boost::none;
99  }
100 
101  return sha512Half (Slice (result.data(), result.size()));
102 }
103 
104 void
106  boost::beast::http::fields& h,
107  ripple::uint256 const& sharedValue,
108  boost::optional<std::uint32_t> networkID,
109  beast::IP::Address public_ip,
110  beast::IP::Address remote_ip,
111  Application& app)
112 {
113  if (networkID)
114  {
115  // The network identifier, if configured, can be used to specify
116  // what network we intend to connect to and detect if the remote
117  // end connects to the same network.
118  h.insert("Network-ID", std::to_string(*networkID));
119  }
120 
121  h.insert ("Network-Time",
122  std::to_string(app.timeKeeper().now().time_since_epoch().count()));
123 
124  h.insert ("Public-Key",
126 
127  {
128  auto const sig = signDigest(app.nodeIdentity().first,
129  app.nodeIdentity().second, sharedValue);
130  h.insert("Session-Signature",
131  base64_encode(sig.data(), sig.size()));
132  }
133 
134  if (beast::IP::is_public (remote_ip))
135  h.insert ("Remote-IP", remote_ip.to_string());
136 
137  if (!public_ip.is_unspecified())
138  h.insert ("Local-IP", public_ip.to_string());
139 
140  if (auto const cl = app.getLedgerMaster().getClosedLedger())
141  {
142  // TODO: Use hex for these
143  h.insert ("Closed-Ledger", base64_encode(
144  cl->info().hash.begin(), cl->info().hash.size()));
145  h.insert ("Previous-Ledger", base64_encode(
146  cl->info().parentHash.begin(), cl->info().parentHash.size()));
147  }
148 }
149 
150 PublicKey
152  boost::beast::http::fields const& headers,
153  ripple::uint256 const& sharedValue,
154  boost::optional<std::uint32_t> networkID,
155  beast::IP::Address public_ip,
156  beast::IP::Address remote,
157  Application& app)
158 {
159  if (networkID)
160  {
161  if (auto const iter = headers.find("Network-ID"); iter != headers.end())
162  {
163  std::uint32_t nid;
164 
165  if (!beast::lexicalCastChecked(nid, iter->value().to_string()))
166  throw std::runtime_error("Invalid peer network identifier");
167 
168  if(nid != *networkID)
169  throw std::runtime_error("Peer is on a different network");
170  }
171  }
172 
173  if (auto const iter = headers.find("Network-Time"); iter != headers.end())
174  {
175  auto const netTime =
176  [str = iter->value().to_string()]() -> TimeKeeper::time_point
177  {
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 = [](
199  {
200  if (a > b)
201  return duration_cast<std::chrono::seconds>(a - b);
202  return - duration_cast<std::chrono::seconds>(b - a);
203  };
204 
205  auto const offset = calculateOffset(netTime, ourTime);
206 
207  if (date::abs(offset) > tolerance)
208  throw std::runtime_error("Peer clock is too far off");
209  }
210 
211  PublicKey const publicKey = [&headers]
212  {
213  if (auto const iter = headers.find ("Public-Key"); iter != headers.end())
214  {
215  auto pk = parseBase58<PublicKey>(
216  TokenType::NodePublic, iter->value().to_string());
217 
218  if(pk)
219  {
220  if (publicKeyType(*pk) != KeyType::secp256k1)
221  throw std::runtime_error("Unsupported public key type");
222 
223  return *pk;
224  }
225  }
226 
227  throw std::runtime_error("Bad node public key");
228  }();
229 
230  if (publicKey == app.nodeIdentity().first)
231  throw std::runtime_error("Self connection");
232 
233  // This check gets two birds with one stone:
234  //
235  // 1) it verifies that the node we are talking to has access to the
236  // private key corresponding to the public node identity it claims.
237  // 2) it verifies that our SSL session is end-to-end with that node
238  // and not through a proxy that establishes two separate sessions.
239  {
240  auto const iter = headers.find("Session-Signature");
241 
242  if (iter == headers.end())
243  throw std::runtime_error("No session signature specified");
244 
245  auto sig = base64_decode(iter->value().to_string());
246 
247  if (! verifyDigest(publicKey, sharedValue, makeSlice(sig), false))
248  throw std::runtime_error("Failed to verify session");
249  }
250 
251  if (auto const iter = headers.find ("Local-IP"); iter != headers.end())
252  {
253  boost::system::error_code ec;
254  auto const local_ip = boost::asio::ip::address::from_string(
255  iter->value().to_string(), ec);
256 
257  if (ec)
258  throw std::runtime_error("Invalid Local-IP");
259 
260  if (beast::IP::is_public(remote) && remote != local_ip)
261  throw std::runtime_error("Incorrect Local-IP: " +
262  remote.to_string() + " instead of " + local_ip.to_string());
263  }
264 
265  if (auto const iter = headers.find("Remote-IP"); iter != headers.end())
266  {
267  boost::system::error_code ec;
268  auto const remote_ip = boost::asio::ip::address::from_string(
269  iter->value().to_string(), ec);
270 
271  if (ec)
272  throw std::runtime_error("Invalid Remote-IP");
273 
274  if (beast::IP::is_public(remote) && !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("Incorrect Remote-IP: " +
280  public_ip.to_string() + " instead of " + remote_ip.to_string());
281  }
282  }
283 
284  return publicKey;
285 }
286 
287 }
ripple::Application
Definition: Application.h:85
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:199
ripple::publicKeyType
boost::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
Definition: PublicKey.cpp:207
ripple::Slice
An immutable linear range of bytes.
Definition: Slice.h:43
ripple::openssl_sha512_hasher
SHA-512 digest.
Definition: digest.h:73
ripple::base64_encode
std::string base64_encode(std::uint8_t const *data, std::size_t len)
Definition: base64.cpp:227
ripple::toBase58
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:29
ripple::stream_type
boost::beast::ssl_stream< socket_type > stream_type
Definition: Handshake.h:40
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:105
ripple::base_uint::data
pointer data()
Definition: base_uint.h:102
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:54
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:151
ripple::base_uint
Definition: base_uint.h:65
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:100
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:73
ripple::base64_decode
std::string base64_decode(std::string const &data)
Definition: base64.cpp:237
beast::Journal::error
Stream error() const
Definition: Journal.h:307
std::runtime_error
STL class.
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:60
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:37
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:222
ripple::KeyType::secp256k1
@ secp256k1
ripple::LedgerMaster::getClosedLedger
std::shared_ptr< Ledger const > getClosedLedger()
Definition: LedgerMaster.h:89
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:262
ripple::sha512Half
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition: digest.h:268
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:62
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:124
std::chrono