Harden MITM detection during session establishment:

Even with TLS encrypted connections, it is possible for a determined
attacker to mount certain types of relatively easy man-in-the-middle
attacks which, if successful, could allow an attacker to tamper with
messages exchanged between endpoints.

The risk can be mitigated if each side has a certificate issued by a
CA that the other side trusts. In the context of a decentralized and
permissionless network, this is neither reasonable nor desirable.

To prevent this problem all we need is to allow the two endpoints, A
and B, to be able to independently verify that they are connected to
each other over a single end-to-end TLS session, instead of separate
TLS sessions which the attacker bridges.

The protocol level handshake implements this security check by using
digital signatures: each endpoint derives a fingerprint from the TLS
session, which it signs with the private key associated with its own
node identity. This strongly binds the TLS session to the identities
of the two endpoints of the session.

This commit introduces a new fingerprint derivation that uses modern
and standardized TLS exporter functionality, instead of the existing
derivation whch uses OpenSSL APIs that are non-standard, and derives
different "incoming" and "outgoing" security cookies.

Lastly, this commit refines the "self-connection" check to allow for
the detection of accidental instances of node identity sharing. This
check was first introduced with #4195 but was partially reverted due
to a bug with #4438. By using distinct security cookies for incoming
and outgoing connections, an attacker is no longer able to claim the
identity of its peer by echoing its security cookie.

The change is backwards compatible and servers with this commit will
still generate and verify old-style fingerprints, in addition to the
new style fingerprints.

For a fuller discussion on this topic, please see:
    https://github.com/openssl/openssl/issues/5509
    https://github.com/ripple/rippled/issues/2413

This commit was previously introduced as #3929, which was closed. If
merged, it also fixes #2413 (which had been closed as a 'WONTFIX').
This commit is contained in:
Nik Bougalis
2021-09-11 01:52:30 -07:00
parent 8b2ef17be3
commit 8a7913a996
13 changed files with 284 additions and 108 deletions

View File

@@ -186,6 +186,8 @@ public:
NodeCache m_tempNodeCache;
CachedSLEs cachedSLEs_;
std::pair<PublicKey, SecretKey> nodeIdentity_;
std::string nodePublicIdentity_;
ValidatorKeys const validatorKeys_;
std::unique_ptr<Resource::Manager> m_resourceManager;
@@ -591,6 +593,12 @@ public:
return nodeIdentity_;
}
std::string const&
getNodePublicIdentity() const override
{
return nodePublicIdentity_;
}
PublicKey const&
getValidationPublicKey() const override
{
@@ -1274,6 +1282,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline)
m_orderBookDB.setup(getLedgerMaster().getCurrentLedger());
nodeIdentity_ = getNodeIdentity(*this, cmdline);
nodePublicIdentity_ = toBase58(TokenType::NodePublic, nodeIdentity().first);
if (!cluster_->load(config().section(SECTION_CLUSTER_NODES)))
{

View File

@@ -242,6 +242,9 @@ public:
virtual std::pair<PublicKey, SecretKey> const&
nodeIdentity() = 0;
virtual std::string const&
getNodePublicIdentity() const = 0;
virtual PublicKey const&
getValidationPublicKey() const = 0;

View File

@@ -548,6 +548,7 @@ public:
using uint128 = base_uint<128>;
using uint160 = base_uint<160>;
using uint256 = base_uint<256>;
using uint512 = base_uint<512>;
template <std::size_t Bits, class Tag>
[[nodiscard]] inline constexpr std::strong_ordering

View File

@@ -65,9 +65,10 @@ Network-ID: 1
Network-Time: 619234489
Public-Key: n94MvLTiHQJjByfGZzvQewTxQP2qjF6shQcuHwCjh5WoiozBrdpX
Session-Signature: MEUCIQCOO8tHOh/tgCSRNe6WwOwmIF6urZ5uSB8l9aAf5q7iRAIgA4aONKBZhpP5RuOuhJP2dP+2UIRioEJcfU4/m4gZdYo=
Session-EKM-Signature: 4F73F4DF23453250BDDF81AFBB5CBF0FD32B92AAF548331DB0ECF1DB988BE2F3D7264621FC1E93404B77612F235AC6FFD4F1B7B4D420E8071384273D9A60201D
Remote-IP: 192.0.2.79
Closed-Ledger: llRZSKqvNieGpPqbFGnm358pmF1aW96SDIUQcnMh6HI=
Previous-Ledger: q4aKbP7sd5wv+EXArwCmQiWZhq9AwBl2p/hCtpGJNsc=
Closed-Ledger: 4F73F45E95343F7446F91B5F70E3AE4E155CCAA2B1CF2F267C99FD39C1C24178
Previous-Ledger: F9D75AAB7D975BADC6D008C4AE94D9B7B6AA323EEE4B133F5B75CE92445C88ED
```
##### Example HTTP Upgrade Response (Success)
@@ -260,18 +261,32 @@ under the specified domain and locating the public key of this server under the
Sending a malformed domain will prevent a connection from being established.
| Field Name | Request | Response |
|-------------------------|:-----------------: |:-----------------: |
| `Session-EKM-Signature` | :heavy_check_mark: | :heavy_check_mark: |
The `Session-EKM-Signature` field supersedes the `Session-Signature` field and is
mandatory if `Session-Signature` is not present. It is used to secure the peer
link against certain types of attack. For more details see the section titled
"Session Security" below.
The value is specified in **HEX** encoding.
| Field Name | Request | Response |
|--------------------- |:-----------------: |:-----------------: |
| `Session-Signature` | :heavy_check_mark: | :heavy_check_mark: |
The `Session-Signature` field is mandatory and is used to secure the peer link
against certain types of attack. For more details see "Session Signature" below.
The `Session-Signature` field is a legacy field that has been superseded by the
`Session-EKM-Signature` field. It will be removed in a future release of the
software.
It is used to secure the peer link against certain types of attack. For more
details see the section titled "Session Signature" below.
The value is presently encoded using **Base64** encoding, but implementations
should support both **Base64** and **HEX** encoding for this value.
For more details on this field, please see **Session Signature** below.
| Field Name | Request | Response |
|--------------------- |:-----------------: |:-----------------: |
@@ -298,7 +313,7 @@ considers to be closed.
The value is encoded as **HEX**, but implementations should support both
**Base64** and **HEX** encoding for this value for legacy purposes.
| Field Name | Request | Response |
|--------------------- |:-----------------: |:-----------------: |
| `Previous-Ledger` | :white_check_mark: | :white_check_mark: |
@@ -306,8 +321,8 @@ The value is encoded as **HEX**, but implementations should support both
If present, identifies the hash of the parent ledger that the sending server
considers to be closed.
The value is presently encoded using **Base64** encoding, but implementations
should support both **Base64** and **HEX** encoding for this value.
The field data should be encoded using **HEX**, but implementations should
correctly interpret both **Base64** and **HEX** encodings.
#### Additional Headers
@@ -318,16 +333,16 @@ Implementations should not reject requests because of the presence of fields
that they do not understand.
### Session Signature
### Session Security
Even for SSL/TLS encrypted connections, it is possible for an attacker to mount
relatively inexpensive MITM attacks that can be extremely hard to detect and
may afford the attacker the ability to intelligently tamper with messages
exchanged between the two endpoints.
This risk can be mitigated if at least one side has a certificate from a certificate
authority trusted by the other endpoint, but having a certificate is not always
possible (or even desirable) in a decentralized and permissionless network.
This risk can be mitigated if at least one side has a certificate from a CA that
is trusted by the other endpoint, but having a certificate is not always
possible (or, indeed, desirable) in a decentralized and permissionless network.
Ultimately, the goal is to ensure that two endpoints A and B know that they are
talking directly to each other over a single end-to-end SSL/TLS session instead
@@ -335,18 +350,14 @@ of two separate SSL/TLS sessions, with an attacker acting as a proxy.
The XRP Ledger protocol prevents this attack by leveraging the fact that the two
servers each have a node identity, in the form of **`secp256k1`** keypairs, and
use that to strongly bind the SSL/TLS session to the node identities of each of
the two servers at the end of the SSL/TLS session.
use that, along with a secure fingerprint associated with the SSL/TLS session to
strongly bind the SSL/TLS session to the node identities of each of the servers
at the end of the SSL/TLS session.
To do this we "reach into" the SSL/TLS session, and extract the **`finished`**
messages for the local and remote endpoints, and combine them to generate a unique
"fingerprint". By design, this fingerprint should be the same for both SSL/TLS
endpoints.
That fingerprint, which is never shared over the wire (since each endpoint will
calculate it independently), is then signed by each server using its public
**`secp256k1`** node identity and the signature is transferred over the SSL/TLS
encrypted link during the protocol handshake phase.
The fingerprint is never shared over the wire (the two endpoints calculate it
independently) and is signed by each server using its public **`secp256k1`**
node identity. The resulting signature is transferred over the SSL/TLS session
during the protocol handshake phase.
Each side of the link will verify that the provided signature is from the claimed
public key against the session's unique fingerprint. If this signature check fails
@@ -365,17 +376,12 @@ message stream between Alice and Bob, although she may be still be able to injec
delays or terminate the link.
# Ripple Clustering #
# Clustering #
A cluster consists of more than one Ripple server under common
administration that share load information, distribute cryptography
operations, and provide greater response consistency.
Cluster nodes are identified by their public node keys. Cluster nodes
exchange information about endpoints that are imposing load upon them.
Cluster nodes share information about their internal load status. Cluster
nodes do not have to verify the cryptographic signatures on messages
received from other cluster nodes.
A cluster consists of several servers, typically under common administration,
that are configured to work cooperatively by sharing server load information,
details about shards, optimizing processing to avoid duplicating work that other
cluster members have done, and more.
## Configuration ##
@@ -385,9 +391,9 @@ beginning with the letter `n`. The key is maintained across runs in a
database.
Cluster members are configured in the `rippled.cfg` file under
`[cluster_nodes]`. Each member should be configured on a line beginning
with the node public key, followed optionally by a space and a friendly
name.
`[cluster_nodes]`. Each member should be configured on a separate line,
beginning with its node public key, followed optionally by a space and a
friendly name.
Because cluster members can introduce other cluster members, it is not
necessary to configure every cluster member on every other cluster member.
@@ -413,11 +419,11 @@ not relay a transaction with an incorrect signature. Validators may wish to
disable this feature, preferring the additional load to get the additional
security of having validators check each transaction.
Local checks for transaction checking are also bypassed. For example, a
server will not reject a transaction from a cluster peer because the fee
does not meet its current relay fee. It is preferable to keep the cluster
in agreement and permit confirmation from one cluster member to more
reliably indicate the transaction's acceptance by the cluster.
Several "local" checks are also bypassed. For example, a server will not reject
a transaction from a cluster peer because the fee does not meet its current
relay fee. It is preferable to keep the cluster in agreement and permit
confirmation from one cluster member to more reliably indicate the transaction's
acceptance by the cluster.
## Server Load Information ##

View File

@@ -197,10 +197,6 @@ ConnectAttempt::onHandshake(error_code ec)
slot_, beast::IPAddressConversion::from_asio(local_endpoint)))
return fail("Duplicate connection");
auto const sharedValue = makeSharedValue(*stream_ptr_, journal_);
if (!sharedValue)
return close(); // makeSharedValue logs
req_ = makeRequest(
!overlay_.peerFinder().config().peerPrivate,
app_.config().COMPRESSION,
@@ -208,15 +204,25 @@ ConnectAttempt::onHandshake(error_code ec)
app_.config().TX_REDUCE_RELAY_ENABLE,
app_.config().VP_REDUCE_RELAY_ENABLE);
auto const sharedValue = makeSharedValue(*stream_ptr_, journal_);
if (!sharedValue)
return close(); // makeSharedValue logs
auto const ekm = getSessionEKM(*stream_ptr_, app_.instanceID(), true);
if (!ekm)
return fail("Unable to retrieve EKM for session");
buildHandshake(
req_,
*sharedValue,
*ekm,
overlay_.setup().networkID,
overlay_.setup().public_ip,
remote_endpoint_.address(),
app_);
setTimer();
boost::beast::http::async_write(
stream_,
req_,
@@ -347,15 +353,37 @@ ConnectAttempt::processResponse()
"processResponse: Unable to negotiate protocol version");
}
auto const sharedValue = makeSharedValue(*stream_ptr_, journal_);
if (!sharedValue)
return close(); // makeSharedValue logs
try
{
auto const sharedValue = makeSharedValue(*stream_ptr_, journal_);
if (!sharedValue)
return close(); // makeSharedValue logs
auto const peerInstanceID = [this]() {
std::uint64_t iid = 0;
if (auto const iter = response_.find("Instance-Cookie");
iter != response_.end())
{
if (!beast::lexicalCastChecked(iid, iter->value().to_string()))
throw std::runtime_error("Invalid instance cookie");
if (iid == 0)
throw std::runtime_error("Invalid instance cookie");
}
return iid;
}();
auto const ekm = getSessionEKM(*stream_ptr_, peerInstanceID, false);
if (!ekm)
return fail("Unable to retrieve EKM for session");
auto publicKey = verifyHandshake(
response_,
*sharedValue,
*ekm,
overlay_.setup().networkID,
overlay_.setup().public_ip,
remote_endpoint_.address(),

View File

@@ -26,12 +26,8 @@
#include <ripple/overlay/impl/Handshake.h>
#include <ripple/protocol/digest.h>
#include <boost/regex.hpp>
#include <algorithm>
#include <chrono>
// VFALCO Shouldn't we have to include the OpenSSL
// headers or something for SSL_get_finished?
namespace ripple {
std::optional<std::string>
@@ -118,10 +114,12 @@ makeFeaturesResponseHeader(
- `SSL_get_peer_finished`.
@return `true` if successful, `false` otherwise.
@note This construct is non-standard. There are potential "standard"
alternatives that should be considered. For a discussion, on
this topic, see https://github.com/openssl/openssl/issues/5509 and
https://github.com/ripple/rippled/issues/2413.
@deprecated This construct is non-standard and is now deprecated in favor
of using `SSL_export_keying_material`. Support for this will
be removed in a future version of the codebase.
For a fuller discussion on this topic, please see:
https://github.com/openssl/openssl/issues/5509
https://github.com/ripple/rippled/issues/2413.
*/
static std::optional<base_uint<512>>
hashLastMessage(SSL const* ssl, size_t (*get)(const SSL*, void*, size_t))
@@ -136,11 +134,33 @@ hashLastMessage(SSL const* ssl, size_t (*get)(const SSL*, void*, size_t))
sha512_hasher h;
base_uint<512> cookie;
uint512 cookie;
SHA512(buf, len, cookie.data());
return cookie;
}
std::optional<uint256>
getSessionEKM(stream_type& ssl, std::uint64_t instance, bool outgoing)
{
std::string const label = std::string("XRPL-EKM-COOKIE:V1:") +
std::to_string(instance) + (outgoing ? ":OUT" : ":IN");
uint256 km;
if (SSL_export_keying_material(
ssl.native_handle(),
km.data(),
km.size(),
label.data(),
label.size(),
nullptr,
0,
0) == 1)
return km;
return std::nullopt;
}
std::optional<uint256>
makeSharedValue(stream_type& ssl, beast::Journal journal)
{
@@ -176,7 +196,8 @@ makeSharedValue(stream_type& ssl, beast::Journal journal)
void
buildHandshake(
boost::beast::http::fields& h,
ripple::uint256 const& sharedValue,
uint256 const& sharedValue,
uint256 const& ekm,
std::optional<std::uint32_t> networkID,
beast::IP::Address public_ip,
beast::IP::Address remote_ip,
@@ -194,9 +215,7 @@ buildHandshake(
"Network-Time",
std::to_string(app.timeKeeper().now().time_since_epoch().count()));
h.insert(
"Public-Key",
toBase58(TokenType::NodePublic, app.nodeIdentity().first));
h.insert("Public-Key", app.getNodePublicIdentity());
{
auto const sig = signDigest(
@@ -204,6 +223,11 @@ buildHandshake(
h.insert("Session-Signature", base64_encode(sig.data(), sig.size()));
}
h.insert(
"Session-EKM-Signature",
strHex(signDigest(
app.nodeIdentity().first, app.nodeIdentity().second, ekm)));
h.insert("Instance-Cookie", std::to_string(app.instanceID()));
if (!app.config().SERVER_DOMAIN.empty())
@@ -225,7 +249,8 @@ buildHandshake(
PublicKey
verifyHandshake(
boost::beast::http::fields const& headers,
ripple::uint256 const& sharedValue,
uint256 const& sharedValue,
uint256 const& ekm,
std::optional<std::uint32_t> networkID,
beast::IP::Address public_ip,
beast::IP::Address remote,
@@ -286,7 +311,7 @@ verifyHandshake(
throw std::runtime_error("Peer clock is too far off");
}
PublicKey const publicKey = [&headers] {
PublicKey const pubKey = [&headers] {
if (auto const iter = headers.find("Public-Key"); iter != headers.end())
{
auto pk = parseBase58<PublicKey>(
@@ -310,20 +335,67 @@ verifyHandshake(
// private key corresponding to the public node identity it claims.
// 2) it verifies that our SSL session is end-to-end with that node
// and not through a proxy that establishes two separate sessions.
//
// Note that if both EKM and legacy style sessions signatures are present
// both must be correct.
bool hasEKM = false;
{
auto const iter = headers.find("Session-Signature");
bool ok = false;
if (iter == headers.end())
throw std::runtime_error("No session signature specified");
if (auto h = headers.find("Session-EKM-Signature");
h != headers.end() && !h->value().empty())
{
if (auto const sig = strUnHex(h->value().to_string()))
{
if (!verifyDigest(pubKey, ekm, {sig->data(), sig->size()}))
throw std::runtime_error("Failed to verify session (EKM)");
auto sig = base64_decode(iter->value().to_string());
hasEKM = true;
ok = true;
}
}
if (!verifyDigest(publicKey, sharedValue, makeSlice(sig), false))
throw std::runtime_error("Failed to verify session");
if (auto h = headers.find("Session-Signature"); h != headers.end())
{
if (auto sig = base64_decode(h->value().to_string()); !sig.empty())
{
if (!verifyDigest(pubKey, sharedValue, makeSlice(sig), false))
throw std::runtime_error("Failed to verify session");
ok = true;
}
}
if (!ok)
throw std::runtime_error("Advanced MITM checks not present");
}
if (publicKey == app.nodeIdentity().first)
throw std::runtime_error("Self connection");
if (pubKey == app.nodeIdentity().first)
{
auto const peerInstanceID = [&headers]() {
std::uint64_t iid = 0;
if (auto const iter = headers.find("Instance-Cookie");
iter != headers.end())
{
if (!beast::lexicalCastChecked(iid, iter->value().to_string()))
throw std::runtime_error("Invalid instance cookie");
if (iid == 0)
throw std::runtime_error("Invalid instance cookie");
}
return iid;
}();
// When EKM is supported, we can be confident that the remote endpoint
// has the same node private key as us.
if (hasEKM && peerInstanceID != app.instanceID())
app.signalStop("Another server is using our node identity");
throw std::runtime_error("Self-connection detected");
}
if (auto const iter = headers.find("Local-IP"); iter != headers.end())
{
@@ -361,7 +433,7 @@ verifyHandshake(
}
}
return publicKey;
return pubKey;
}
auto
@@ -398,6 +470,7 @@ makeResponse(
beast::IP::Address public_ip,
beast::IP::Address remote_ip,
uint256 const& sharedValue,
uint256 const& ekm,
std::optional<std::uint32_t> networkID,
ProtocolVersion protocol,
Application& app)
@@ -419,7 +492,8 @@ makeResponse(
app.config().TX_REDUCE_RELAY_ENABLE,
app.config().VP_REDUCE_RELAY_ENABLE));
buildHandshake(resp, sharedValue, networkID, public_ip, remote_ip, app);
buildHandshake(
resp, sharedValue, ekm, networkID, public_ip, remote_ip, app);
return resp;
}

View File

@@ -48,6 +48,23 @@ using http_request_type =
using http_response_type =
boost::beast::http::response<boost::beast::http::dynamic_body>;
/** Returns a value shared by the two endpoints of a TLS-secured connection.
This value is generated in a secure fashion and is never communicated over
the wire, even over an encrypted connection. Used properly, it can help to
detect and prevent preventing active MITM attacks.
@param ssl the SSL/TLS connection state.
@param instance a 64-bit cookie, used in computing the shared value.
@param outgoing true to return the "outgoing" value is needed; false for
the "incoming" value.
@return On success, the 256-bit value this side believes both endpoints
share; an unseated optional otherwise.
*/
[[nodiscard]] std::optional<uint256>
getSessionEKM(stream_type& ssl, std::uint64_t instance, bool outgoing);
/** Computes a shared value based on the SSL connection state.
When there is no man in the middle, both sides will compute the same
@@ -60,32 +77,61 @@ using http_response_type =
std::optional<uint256>
makeSharedValue(stream_type& ssl, beast::Journal journal);
/** Insert fields headers necessary for upgrading the link to the peer protocol.
/** Populate header fields needed when upgrading the link to the peer protocol.
Some of the fields are used in critical security checks that can prevent
active MITM attacks and ensure that the remote peer has the private keys
that correspond to the public identity it claims.
@param h the list of HTTP headers fields to send.
@param sharedValue a 256-bit value derived from the SSL session (legacy).
@param ekm a 256-bit value derived from the SSL session.
@param networkID the identifier of the network the server is configured for.
@param public_ip The server's public IP.
@param remote_ip The IP to which the server attempted to connect.
@param app The main application object.
@note The `sharedValue` parameter is deprecated and will be removed in a
future version of the code. It is replaced by `ekm` which is derived
in a more standardized function.
\sa makeSharedValue, getSessionEKM
*/
void
buildHandshake(
boost::beast::http::fields& h,
uint256 const& sharedValue,
uint256 const& ekm,
std::optional<std::uint32_t> networkID,
beast::IP::Address public_ip,
beast::IP::Address remote_ip,
Application& app);
/** Validate header fields necessary for upgrading the link to the peer
protocol.
/** Validate header fields needed when upgrading the link to the peer protocol.
This performs critical security checks that ensure that prevent
MITM attacks on our peer-to-peer links and that the remote peer
has the private keys that correspond to the public identity it
claims.
Some of the fields are used in critical security checks that can prevent
active MITM attacks and ensure that the remote peer has the private keys
that correspond to the public identity it claims.
@return The public key of the remote peer.
@throw A class derived from std::exception.
@param h the list of HTTP headers fields we received.
@param sharedValue a 256-bit value derived from the SSL session (legacy).
@param ekm a 256-bit value derived from the SSL session.
@param networkID the identifier of the network the server is configured for.
@param public_ip The server's public IP.
@param remote_ip The IP to which the server attempted to connect.
@param app The main application object.
@return The public key of the remote peer on success. An exception
otherwise.
@throw A class derived from std::exception, with an appropriate error
message.
*/
PublicKey
[[nodiscard]] PublicKey
verifyHandshake(
boost::beast::http::fields const& headers,
uint256 const& sharedValue,
uint256 const& ekm,
std::optional<std::uint32_t> networkID,
beast::IP::Address public_ip,
beast::IP::Address remote,
@@ -117,6 +163,7 @@ makeRequest(
@param public_ip server's public IP
@param remote_ip peer's IP
@param sharedValue shared value based on the SSL connection state
@param ekm shared value based on the SSL connection state
@param networkID specifies what network we intend to connect to
@param version supported protocol version
@param app Application's reference to access some common properties
@@ -129,6 +176,7 @@ makeResponse(
beast::IP::Address public_ip,
beast::IP::Address remote_ip,
uint256 const& sharedValue,
uint256 const& ekm,
std::optional<std::uint32_t> networkID,
ProtocolVersion version,
Application& app);

View File

@@ -248,11 +248,27 @@ OverlayImpl::onHandoff(
return handoff;
}
auto const ekm = getSessionEKM(*stream_ptr, app_.instanceID(), true);
if (!ekm)
{
m_peerFinder->on_closed(slot);
handoff.moved = false;
handoff.response = makeErrorResponse(
slot,
request,
remote_endpoint.address(),
"Session EKM is unavailable");
handoff.keep_alive = false;
return handoff;
}
try
{
auto publicKey = verifyHandshake(
request,
*sharedValue,
*ekm,
setup_.networkID,
setup_.public_ip,
remote_endpoint.address(),
@@ -677,8 +693,7 @@ OverlayImpl::crawlShards(bool includePublicKey, std::uint32_t relays)
if (auto shardStore = app_.getShardStore())
{
if (includePublicKey)
jv[jss::public_key] =
toBase58(TokenType::NodePublic, app_.nodeIdentity().first);
jv[jss::public_key] = app_.getNodePublicIdentity();
auto const shardInfo{shardStore->getShardInfo()};
if (!shardInfo->finalized().empty())

View File

@@ -603,6 +603,7 @@ PeerImp::fail(std::string const& reason)
JLOG(journal_.warn()) << (n.empty() ? remote_address_.to_string() : n)
<< " failed: " << reason;
}
close();
}
@@ -758,6 +759,7 @@ void
PeerImp::doAccept()
{
assert(read_buffer_.size() == 0);
assert(!inbound_);
JLOG(journal_.debug()) << "doAccept: " << remote_address_;
@@ -768,6 +770,11 @@ PeerImp::doAccept()
if (!sharedValue)
return fail("makeSharedValue: Unexpected failure");
auto const ekm = getSessionEKM(*stream_ptr_, app_.instanceID(), !inbound_);
if (!ekm)
return fail("getSessionEKM: Unexpected failure");
JLOG(journal_.info()) << "Protocol: " << to_string(protocol_);
JLOG(journal_.info()) << "Public Key: "
<< toBase58(TokenType::NodePublic, publicKey_);
@@ -795,6 +802,7 @@ PeerImp::doAccept()
overlay_.setup().public_ip,
remote_address_.address(),
*sharedValue,
*ekm,
overlay_.setup().networkID,
protocol_,
app_);

View File

@@ -251,26 +251,7 @@ sign(PublicKey const& pk, SecretKey const& sk, Slice const& m)
case KeyType::secp256k1: {
sha512_half_hasher h;
h(m.data(), m.size());
auto const digest = sha512_half_hasher::result_type(h);
secp256k1_ecdsa_signature sig_imp;
if (secp256k1_ecdsa_sign(
secp256k1Context(),
&sig_imp,
reinterpret_cast<unsigned char const*>(digest.data()),
reinterpret_cast<unsigned char const*>(sk.data()),
secp256k1_nonce_function_rfc6979,
nullptr) != 1)
LogicError("sign: secp256k1_ecdsa_sign failed");
unsigned char sig[72];
size_t len = sizeof(sig);
if (secp256k1_ecdsa_signature_serialize_der(
secp256k1Context(), sig, &len, &sig_imp) != 1)
LogicError(
"sign: secp256k1_ecdsa_signature_serialize_der failed");
return Buffer{sig, len};
return signDigest(pk, sk, sha512_half_hasher::result_type(h));
}
default:
LogicError("sign: invalid type");

View File

@@ -1100,6 +1100,7 @@ struct LedgerReplayer_test : public beast::unit_test::suite
addr,
addr,
uint256{1},
uint256{1},
1,
{1, 0},
serverEnv.app());

View File

@@ -511,6 +511,7 @@ public:
addr,
addr,
uint256{1},
uint256{1},
1,
{1, 0},
env->app());

View File

@@ -1534,6 +1534,7 @@ vp_squelched=1
addr,
addr,
uint256{1},
uint256{1},
1,
{1, 0},
env_.app());
@@ -1597,4 +1598,4 @@ BEAST_DEFINE_TESTSUITE_MANUAL(reduce_relay_simulate, ripple_data, ripple);
} // namespace test
} // namespace ripple
} // namespace ripple