Browse Source

Noise::write now reports the correct amount of bytes (#99)

* Noise::write now reports the correct amount of bytes
* Fix data handling in Noise::read() & Noise::readSome()

Signed-off-by: Igor Egorov <igor@soramitsu.co.jp>
pull/174/head
Igor Egorov 3 years ago
committed by GitHub
parent
commit
e13f051eb5
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 26
      include/libp2p/security/noise/noise_connection.hpp
  2. 6
      src/common/hexutil.cpp
  3. 23
      src/crypto/common_functions.cpp
  4. 4
      src/crypto/key_marshaller/key_marshaller_impl.cpp
  5. 6
      src/security/noise/handshake_message_marshaller_impl.cpp
  6. 107
      src/security/noise/noise_connection.cpp

26
include/libp2p/security/noise/noise_connection.hpp

@ -6,6 +6,8 @@
#ifndef LIBP2P_INCLUDE_LIBP2P_SECURITY_NOISE_NOISE_CONNECTION_HPP #ifndef LIBP2P_INCLUDE_LIBP2P_SECURITY_NOISE_NOISE_CONNECTION_HPP
#define LIBP2P_INCLUDE_LIBP2P_SECURITY_NOISE_NOISE_CONNECTION_HPP #define LIBP2P_INCLUDE_LIBP2P_SECURITY_NOISE_NOISE_CONNECTION_HPP
#include <list>
#include <libp2p/connection/secure_connection.hpp> #include <libp2p/connection/secure_connection.hpp>
#include <libp2p/common/metrics/instance_count.hpp> #include <libp2p/common/metrics/instance_count.hpp>
@ -22,6 +24,14 @@ namespace libp2p::connection {
class NoiseConnection : public SecureConnection, class NoiseConnection : public SecureConnection,
public std::enable_shared_from_this<NoiseConnection> { public std::enable_shared_from_this<NoiseConnection> {
public: public:
using BufferList = std::list<common::ByteArray>;
struct OperationContext {
size_t bytes_served; /// written or read bytes count
const size_t total_bytes; /// total size to process
BufferList::iterator write_buffer; /// temporary data storage
};
~NoiseConnection() override = default; ~NoiseConnection() override = default;
NoiseConnection( NoiseConnection(
@ -65,6 +75,17 @@ namespace libp2p::connection {
outcome::result<crypto::PublicKey> remotePublicKey() const override; outcome::result<crypto::PublicKey> remotePublicKey() const override;
private: private:
void read(gsl::span<uint8_t> out, size_t bytes, OperationContext ctx,
ReadCallbackFunc cb);
void readSome(gsl::span<uint8_t> out, size_t bytes, OperationContext ctx,
ReadCallbackFunc cb);
void write(gsl::span<const uint8_t> in, size_t bytes, OperationContext ctx,
WriteCallbackFunc cb);
void eraseWriteBuffer(BufferList::iterator &iterator);
std::shared_ptr<RawConnection> raw_connection_; std::shared_ptr<RawConnection> raw_connection_;
crypto::PublicKey local_; crypto::PublicKey local_;
crypto::PublicKey remote_; crypto::PublicKey remote_;
@ -73,10 +94,7 @@ namespace libp2p::connection {
std::shared_ptr<security::noise::CipherState> decoder_cs_; std::shared_ptr<security::noise::CipherState> decoder_cs_;
std::shared_ptr<common::ByteArray> frame_buffer_; std::shared_ptr<common::ByteArray> frame_buffer_;
std::shared_ptr<security::noise::InsecureReadWriter> framer_; std::shared_ptr<security::noise::InsecureReadWriter> framer_;
size_t already_read_; BufferList write_buffers_;
size_t already_wrote_;
size_t plaintext_len_to_write_;
common::ByteArray writing_;
log::Logger log_ = log::createLogger("NoiseConnection"); log::Logger log_ = log::createLogger("NoiseConnection");
public: public:

6
src/common/hexutil.cpp

@ -3,6 +3,8 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#include <ios>
#include <libp2p/common/hexutil.hpp> #include <libp2p/common/hexutil.hpp>
#include <boost/algorithm/hex.hpp> #include <boost/algorithm/hex.hpp>
@ -24,13 +26,13 @@ namespace libp2p::common {
std::string int_to_hex(uint64_t n, size_t fixed_width) noexcept { std::string int_to_hex(uint64_t n, size_t fixed_width) noexcept {
std::stringstream result; std::stringstream result;
result.width(fixed_width); result.width(static_cast<std::streamsize>(fixed_width));
result.fill('0'); result.fill('0');
result << std::hex << std::uppercase << n; result << std::hex << std::uppercase << n;
auto str = result.str(); auto str = result.str();
if (str.length() % 2 != 0) { if (str.length() % 2 != 0) {
str.push_back('\0'); str.push_back('\0');
for (int64_t i = str.length() - 2; i >= 0; --i) { for (int64_t i = static_cast<int64_t>(str.length()) - 2; i >= 0; --i) {
str[i + 1] = str[i]; str[i + 1] = str[i];
} }
str[0] = '0'; str[0] = '0';

23
src/crypto/common_functions.cpp

@ -25,8 +25,8 @@ namespace libp2p::crypto {
} }
// turn private key bytes into big number // turn private key bytes into big number
BIGNUM *private_bignum{ BIGNUM *private_bignum{BN_bin2bn(
BN_bin2bn(private_key.data(), private_key.size(), nullptr)}; private_key.data(), static_cast<int>(private_key.size()), nullptr)};
if (nullptr == private_bignum) { if (nullptr == private_bignum) {
return FAILED; return FAILED;
} }
@ -92,10 +92,11 @@ namespace libp2p::crypto {
int, gsl::span<const uint8_t>, decltype(EVP_PKEY_new_raw_public_key) *); int, gsl::span<const uint8_t>, decltype(EVP_PKEY_new_raw_public_key) *);
outcome::result<std::vector<uint8_t>> GenerateEcSignature( outcome::result<std::vector<uint8_t>> GenerateEcSignature(
gsl::span<const uint8_t> digest, gsl::span<const uint8_t> digest, const std::shared_ptr<EC_KEY> &key) {
const std::shared_ptr<EC_KEY> &key) {
std::shared_ptr<ECDSA_SIG> signature{ std::shared_ptr<ECDSA_SIG> signature{
ECDSA_do_sign(digest.data(), digest.size(), key.get()), ECDSA_SIG_free}; ECDSA_do_sign(digest.data(), static_cast<int>(digest.size()),
key.get()),
ECDSA_SIG_free};
if (signature == nullptr) { if (signature == nullptr) {
return CryptoProviderError::SIGNATURE_GENERATION_FAILED; return CryptoProviderError::SIGNATURE_GENERATION_FAILED;
} }
@ -110,12 +111,12 @@ namespace libp2p::crypto {
return std::move(signature_bytes); return std::move(signature_bytes);
} }
outcome::result<bool> VerifyEcSignature( outcome::result<bool> VerifyEcSignature(gsl::span<const uint8_t> digest,
gsl::span<const uint8_t> digest, gsl::span<const uint8_t> signature,
gsl::span<const uint8_t> signature, const std::shared_ptr<EC_KEY> &key) {
const std::shared_ptr<EC_KEY> &key) { int result = ECDSA_verify(0, digest.data(), static_cast<int>(digest.size()),
int result = ECDSA_verify(0, digest.data(), digest.size(), signature.data(), signature.data(),
signature.size(), key.get()); static_cast<int>(signature.size()), key.get());
if (result < 0) { if (result < 0) {
return CryptoProviderError::SIGNATURE_VERIFICATION_FAILED; return CryptoProviderError::SIGNATURE_VERIFICATION_FAILED;
} }

4
src/crypto/key_marshaller/key_marshaller_impl.cpp

@ -84,7 +84,7 @@ namespace libp2p::crypto::marshaller {
const ProtobufKey &proto_key) const { const ProtobufKey &proto_key) const {
protobuf::PublicKey protobuf_key; protobuf::PublicKey protobuf_key;
if (!protobuf_key.ParseFromArray(proto_key.key.data(), if (!protobuf_key.ParseFromArray(proto_key.key.data(),
proto_key.key.size())) { static_cast<int>(proto_key.key.size()))) {
return CryptoProviderError::FAILED_UNMARSHAL_DATA; return CryptoProviderError::FAILED_UNMARSHAL_DATA;
} }
@ -101,7 +101,7 @@ namespace libp2p::crypto::marshaller {
const ProtobufKey &proto_key) const { const ProtobufKey &proto_key) const {
protobuf::PublicKey protobuf_key; protobuf::PublicKey protobuf_key;
if (!protobuf_key.ParseFromArray(proto_key.key.data(), if (!protobuf_key.ParseFromArray(proto_key.key.data(),
proto_key.key.size())) { static_cast<int>(proto_key.key.size()))) {
return CryptoProviderError::FAILED_UNMARSHAL_DATA; return CryptoProviderError::FAILED_UNMARSHAL_DATA;
} }

6
src/security/noise/handshake_message_marshaller_impl.cpp

@ -59,7 +59,8 @@ namespace libp2p::security::noise {
const HandshakeMessage &msg) const { const HandshakeMessage &msg) const {
OUTCOME_TRY(proto_msg, handyToProto(msg)); OUTCOME_TRY(proto_msg, handyToProto(msg));
common::ByteArray out_msg(proto_msg.ByteSizeLong()); common::ByteArray out_msg(proto_msg.ByteSizeLong());
if (not proto_msg.SerializeToArray(out_msg.data(), out_msg.size())) { if (not proto_msg.SerializeToArray(out_msg.data(),
static_cast<int>(out_msg.size()))) {
return Error::MESSAGE_SERIALIZING_ERROR; return Error::MESSAGE_SERIALIZING_ERROR;
} }
return out_msg; return out_msg;
@ -69,7 +70,8 @@ namespace libp2p::security::noise {
HandshakeMessageMarshallerImpl::unmarshal( HandshakeMessageMarshallerImpl::unmarshal(
gsl::span<const uint8_t> msg_bytes) const { gsl::span<const uint8_t> msg_bytes) const {
protobuf::NoiseHandshakePayload proto_msg; protobuf::NoiseHandshakePayload proto_msg;
if (not proto_msg.ParseFromArray(msg_bytes.data(), msg_bytes.size())) { if (not proto_msg.ParseFromArray(msg_bytes.data(),
static_cast<int>(msg_bytes.size()))) {
return Error::MESSAGE_DESERIALIZING_ERROR; return Error::MESSAGE_DESERIALIZING_ERROR;
} }
return protoToHandy(proto_msg); return protoToHandy(proto_msg);

107
src/security/noise/noise_connection.cpp

@ -12,10 +12,11 @@
#define UNIQUE_NAME(base) base##__LINE__ #define UNIQUE_NAME(base) base##__LINE__
#endif // UNIQUE_NAME #endif // UNIQUE_NAME
#define OUTCOME_CB_I(var, res) \ #define OUTCOME_CB_I(var, res) \
auto && (var) = (res); \ auto && (var) = (res); \
if ((var).has_error()) { \ if ((var).has_error()) { \
return cb((var).error()); \ self->eraseWriteBuffer(ctx.write_buffer); \
return cb((var).error()); \
} }
#define OUTCOME_CB_NAME_I(var, val, res) \ #define OUTCOME_CB_NAME_I(var, val, res) \
@ -40,10 +41,7 @@ namespace libp2p::connection {
frame_buffer_{ frame_buffer_{
std::make_shared<common::ByteArray>(security::noise::kMaxMsgLen)}, std::make_shared<common::ByteArray>(security::noise::kMaxMsgLen)},
framer_{std::make_shared<security::noise::InsecureReadWriter>( framer_{std::make_shared<security::noise::InsecureReadWriter>(
raw_connection_, frame_buffer_)}, raw_connection_, frame_buffer_)} {
already_read_{0},
already_wrote_{0},
plaintext_len_to_write_{0} {
BOOST_ASSERT(raw_connection_); BOOST_ASSERT(raw_connection_);
BOOST_ASSERT(key_marshaller_); BOOST_ASSERT(key_marshaller_);
BOOST_ASSERT(encoder_cs_); BOOST_ASSERT(encoder_cs_);
@ -63,63 +61,90 @@ namespace libp2p::connection {
void NoiseConnection::read(gsl::span<uint8_t> out, size_t bytes, void NoiseConnection::read(gsl::span<uint8_t> out, size_t bytes,
libp2p::basic::Reader::ReadCallbackFunc cb) { libp2p::basic::Reader::ReadCallbackFunc cb) {
OperationContext context{.bytes_served = 0,
.total_bytes = bytes,
.write_buffer = write_buffers_.end()};
read(out, bytes, context, std::move(cb));
}
void NoiseConnection::read(gsl::span<uint8_t> out, size_t bytes,
OperationContext ctx, ReadCallbackFunc cb) {
size_t out_size{out.empty() ? 0u : static_cast<size_t>(out.size())}; size_t out_size{out.empty() ? 0u : static_cast<size_t>(out.size())};
BOOST_ASSERT(out_size >= bytes); BOOST_ASSERT(out_size >= bytes);
if (bytes == 0) { if (0 == bytes) {
auto n{already_read_}; BOOST_ASSERT(ctx.bytes_served == ctx.total_bytes);
already_read_ = 0; return cb(ctx.bytes_served);
return cb(n);
} }
readSome(out, bytes, readSome(out, bytes,
[self{shared_from_this()}, out, bytes, [self{shared_from_this()}, out, bytes, cb{std::move(cb)},
cb{std::move(cb)}](auto _n) mutable { ctx](auto _n) mutable {
OUTCOME_CB(n, _n); OUTCOME_CB(n, _n);
self->already_read_ += n; ctx.bytes_served += n;
self->read(out.subspan(n), bytes - n, std::move(cb)); self->read(out.subspan(n), bytes - n, ctx, std::move(cb));
}); });
} }
void NoiseConnection::readSome(gsl::span<uint8_t> out, size_t bytes, void NoiseConnection::readSome(gsl::span<uint8_t> out, size_t bytes,
libp2p::basic::Reader::ReadCallbackFunc cb) { libp2p::basic::Reader::ReadCallbackFunc cb) {
OperationContext context{.bytes_served = 0,
.total_bytes = bytes,
.write_buffer = write_buffers_.end()};
readSome(out, bytes, context, std::move(cb));
}
void NoiseConnection::readSome(gsl::span<uint8_t> out, size_t bytes,
OperationContext ctx, ReadCallbackFunc cb) {
if (not frame_buffer_->empty()) { if (not frame_buffer_->empty()) {
auto n{std::min(bytes, frame_buffer_->size())}; auto n{std::min(bytes, frame_buffer_->size())};
auto begin{frame_buffer_->begin()}; auto begin{frame_buffer_->begin()};
auto end{begin + n}; auto end{begin + static_cast<int64_t>(n)};
std::copy(begin, end, out.begin()); std::copy(begin, end, out.begin());
frame_buffer_->erase(begin, end); frame_buffer_->erase(begin, end);
return cb(n); return cb(n);
} }
framer_->read([self{shared_from_this()}, out, bytes, framer_->read([self{shared_from_this()}, out, bytes, cb{std::move(cb)},
cb{std::move(cb)}](auto _data) mutable { ctx](auto _data) mutable {
OUTCOME_CB(data, _data); OUTCOME_CB(data, _data);
OUTCOME_CB(decrypted, self->decoder_cs_->decrypt({}, *data, {})); OUTCOME_CB(decrypted, self->decoder_cs_->decrypt({}, *data, {}));
self->frame_buffer_->assign(decrypted.begin(), decrypted.end()); self->frame_buffer_->assign(decrypted.begin(), decrypted.end());
self->readSome(out, bytes, std::move(cb)); self->readSome(out, bytes, ctx, std::move(cb));
}); });
} }
void NoiseConnection::write(gsl::span<const uint8_t> in, size_t bytes, void NoiseConnection::write(gsl::span<const uint8_t> in, size_t bytes,
libp2p::basic::Writer::WriteCallbackFunc cb) { libp2p::basic::Writer::WriteCallbackFunc cb) {
if (0 == plaintext_len_to_write_) { OperationContext context{.bytes_served = 0,
plaintext_len_to_write_ = bytes; .total_bytes = bytes,
} .write_buffer = write_buffers_.end()};
if (bytes == 0) { write(in, bytes, context, std::move(cb));
BOOST_ASSERT(already_wrote_ >= plaintext_len_to_write_); }
auto n{plaintext_len_to_write_};
already_wrote_ = 0; void NoiseConnection::write(gsl::span<const uint8_t> in, size_t bytes,
plaintext_len_to_write_ = 0; NoiseConnection::OperationContext ctx,
return cb(n); basic::Writer::WriteCallbackFunc cb) {
auto *self{this}; // for OUTCOME_CB
if (0 == bytes) {
BOOST_ASSERT(ctx.bytes_served >= ctx.total_bytes);
eraseWriteBuffer(ctx.write_buffer);
return cb(ctx.total_bytes);
} }
auto n{std::min(bytes, security::noise::kMaxPlainText)}; auto n{std::min(bytes, security::noise::kMaxPlainText)};
OUTCOME_CB(encrypted, encoder_cs_->encrypt({}, in.subspan(0, n), {})); OUTCOME_CB(encrypted, encoder_cs_->encrypt({}, in.subspan(0, n), {}));
writing_ = std::move(encrypted); if (write_buffers_.end() == ctx.write_buffer) {
framer_->write(writing_, constexpr auto dummy_size = 1;
[self{shared_from_this()}, in{in.subspan(n)}, constexpr auto dummy_value = 0x0;
bytes{bytes - n}, cb{std::move(cb)}](auto _n) mutable { ctx.write_buffer =
OUTCOME_CB(n, _n); write_buffers_.emplace(write_buffers_.end(), dummy_size, dummy_value);
self->already_wrote_ += n; }
self->write(in, bytes, std::move(cb)); ctx.write_buffer->swap(encrypted);
}); framer_->write(
*ctx.write_buffer,
[self{shared_from_this()}, in{in.subspan(static_cast<int64_t>(n))},
bytes{bytes - n}, cb{std::move(cb)}, ctx](auto _n) mutable {
OUTCOME_CB(n, _n);
ctx.bytes_served += n;
self->write(in, bytes, ctx, std::move(cb));
});
} }
void NoiseConnection::writeSome(gsl::span<const uint8_t> in, size_t bytes, void NoiseConnection::writeSome(gsl::span<const uint8_t> in, size_t bytes,
@ -165,4 +190,12 @@ namespace libp2p::connection {
const { const {
return remote_; return remote_;
} }
void NoiseConnection::eraseWriteBuffer(BufferList::iterator &iterator) {
if (write_buffers_.end() == iterator) {
return;
}
write_buffers_.erase(iterator);
iterator = write_buffers_.end();
}
} // namespace libp2p::connection } // namespace libp2p::connection

Loading…
Cancel
Save