Skip to content

Commit

Permalink
WIP: add Sv2Transport
Browse files Browse the repository at this point in the history
Implemented starting from a copy of V2Transport and the V2TransportTester, modifying it to fit the Stratum v2 and Noise Protocol requirements.

Currently unused.
  • Loading branch information
Sjors committed Jan 4, 2024
1 parent ad84ca9 commit 02bc9fd
Show file tree
Hide file tree
Showing 10 changed files with 1,149 additions and 22 deletions.
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ BITCOIN_CORE_H = \
common/run_command.h \
common/sv2_messages.h \
common/sv2_noise.h \
common/sv2_transport.h \
common/url.h \
common/v1_transport.h \
common/v2_transport.h \
Expand Down Expand Up @@ -685,6 +686,7 @@ libbitcoin_common_a_SOURCES = \
common/sv2_messages.cpp \
common/sv2_noise.cpp \
common/settings.cpp \
common/sv2_transport.cpp \
common/system.cpp \
common/transport.cpp \
common/v1_transport.cpp \
Expand Down
1 change: 1 addition & 0 deletions src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ BITCOIN_TESTS =\
test/sv2_noise_tests.cpp \
test/sv2_messages_tests.cpp \
test/sv2_template_provider_tests.cpp \
test/sv2_transport_tests.cpp \
test/timedata_tests.cpp \
test/torcontrol_tests.cpp \
test/transaction_tests.cpp \
Expand Down
6 changes: 6 additions & 0 deletions src/common/sv2_messages.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
// Copyright (c) 2023-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_COMMON_SV2_MESSAGES_H
#define BITCOIN_COMMON_SV2_MESSAGES_H

#include <consensus/validation.h>
#include <cstdint>
#include <net_message.h>
#include <primitives/transaction.h>
#include <script/script.h>
#include <streams.h>
Expand Down Expand Up @@ -637,6 +642,7 @@ class Sv2NetMsg
s.write(MakeByteSpan(m_msg));
}
};

}

#endif // BITCOIN_COMMON_SV2_MESSAGES_H
60 changes: 48 additions & 12 deletions src/common/sv2_noise.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,13 @@ Sv2CipherState::Sv2CipherState(uint8_t key[KEY_SIZE])
std::copy(key, key + KEY_SIZE, m_key);
}

void Sv2CipherState::DecryptWithAd(Span<const std::byte> associated_data, Span<std::byte> msg)
bool Sv2CipherState::DecryptWithAd(Span<const std::byte> associated_data, Span<std::byte> msg)
{
AEADChaCha20Poly1305::Nonce96 nonce = {0, ++m_nonce};

auto key = MakeByteSpan(Span(m_key));
AEADChaCha20Poly1305 aead{key};
bool res = aead.Decrypt(msg, associated_data, nonce, Span(msg.begin(), msg.end() - POLY1305_TAGLEN));
if (!res) {
throw std::runtime_error("Sv2CipherState::DecryptWithAd(): Failed to decrypt message.\n");
}
return aead.Decrypt(msg, associated_data, nonce, Span(msg.begin(), msg.end() - POLY1305_TAGLEN));
}

// The encryption assumes that the msg variable has sufficient space for a 16 byte MAC.
Expand Down Expand Up @@ -104,14 +101,14 @@ void Sv2CipherState::EncryptMessage(Span<std::byte> input, Span<std::byte> outpu
Assume(bytes_written == output.size());
}

void Sv2CipherState::DecryptMessage(Span<std::byte> message) {
bool Sv2CipherState::DecryptMessage(Span<std::byte> message) {
size_t processed = 0;
std::vector<std::byte> ad; // No associated data

while (processed < message.size()) {
size_t chunk_size = std::min(message.size() - processed, NOISE_MAX_CHUNK_SIZE);
Span<std::byte> chunk = message.subspan(processed, chunk_size);
DecryptWithAd(ad, chunk);
if (!DecryptWithAd(ad, chunk)) return false;
processed += chunk_size;
}
}
Expand Down Expand Up @@ -161,12 +158,13 @@ void Sv2SymmetricState::EncryptAndHash(Span<std::byte> data)
MixHash(data);
}

void Sv2SymmetricState::DecryptAndHash(Span<std::byte> data)
bool Sv2SymmetricState::DecryptAndHash(Span<std::byte> data)
{
// The handshake requires mix hashing the cipher text NOT the decrypted
// plaintext.
std::vector<std::byte> cipher_text(data.begin(), data.end());
m_cipher_state.DecryptWithAd(MakeByteSpan(m_hash_output), data);
bool res = m_cipher_state.DecryptWithAd(MakeByteSpan(m_hash_output), data);
if (!res) return false;
MixHash(cipher_text);
}

Expand Down Expand Up @@ -317,6 +315,44 @@ void Sv2HandshakeState::ReadMsgES(Span<std::byte> msg)
bytes_read += (digital_sig_len + POLY1305_TAGLEN);
}

Sv2Cipher::Sv2Cipher(CKey&& static_key, bool initiator)
{
m_handshake_state = Sv2HandshakeState(std::move(static_key));
m_initiator = initiator;
}


void Sv2Cipher::FinishHandshake()
{
auto cipher_state = m_handshake_state.m_symmetric_state.Split();
auto cs1 = cipher_state[0];
auto cs2 = cipher_state[1];

m_hash = std::move(m_handshake_state.m_symmetric_state.m_hash_output);
m_cs1 = std::move(cs1);
m_cs2 = std::move(cs2);
}

bool Sv2Cipher::DecryptMessage(Span<std::byte> message)
{
if (m_initiator) {
return m_cs2.DecryptMessage(message);
} else {
return m_cs1.DecryptMessage(message);
}
}

void Sv2Cipher::EncryptMessage(Span<std::byte> input, Span<std::byte> output)
{
Assume(output.size() == Sv2NoiseSession::EncryptedMessageSize(input.size()));

if (m_initiator) {
m_cs1.EncryptMessage(input, output);
} else {
m_cs2.EncryptMessage(input, output);
}
}

void Sv2NoiseSession::ProcessMaybeHandshake(Span<std::byte> msg, bool send)
{
switch (m_session_state)
Expand Down Expand Up @@ -380,14 +416,14 @@ void Sv2NoiseSession::EncryptMessage(Span<std::byte> input, Span<std::byte> outp
}
}

void Sv2NoiseSession::DecryptMessage(Span<std::byte> message)
bool Sv2NoiseSession::DecryptMessage(Span<std::byte> message)
{
Assume(m_session_state == SessionState::TRANSPORT);

if (m_initiator) {
m_cs2.DecryptMessage(message);
return m_cs2.DecryptMessage(message);
} else {
m_cs1.DecryptMessage(message);
return m_cs1.DecryptMessage(message);
}
}

Expand Down
38 changes: 34 additions & 4 deletions src/common/sv2_noise.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class Sv2SignatureNoiseMessage
}
};

/** TODO: delete enum, state machine is handled by Sv2Transport */
enum class SessionState
{
// The first step of the handshake expects the initiator to send a msg E.
Expand All @@ -78,7 +79,13 @@ class Sv2CipherState
Sv2CipherState() = default;
explicit Sv2CipherState(uint8_t key[KEY_SIZE]);

void DecryptWithAd(Span<const std::byte> associated_data, Span<std::byte> msg);
/** Decrypt message
* @param[in] associated_data associated data
* @param[in,out] msg message with encrypted and authenticated chunks
*
* @returns whether decryption succeeded
*/
[[nodiscard]] bool DecryptWithAd(Span<const std::byte> associated_data, Span<std::byte> msg);
void EncryptWithAd(Span<const std::byte> associated_data, Span<std::byte> msg);

/** The message will be chunked in NOISE_MAX_CHUNK_SIZE parts and expanded
Expand All @@ -93,7 +100,7 @@ class Sv2CipherState
*
* @param[in] message message
*/
void DecryptMessage(Span<std::byte> message);
[[ nodiscard ]] bool DecryptMessage(Span<std::byte> message);

private:
uint8_t m_key[KEY_SIZE];
Expand All @@ -114,7 +121,7 @@ class Sv2SymmetricState
void MixHash(const Span<const std::byte> input);
void MixKey(const Span<const uint8_t> input_key_material);
void EncryptAndHash(Span<std::byte> data);
void DecryptAndHash(Span<std::byte> data);
[[ nodiscard ]] bool DecryptAndHash(Span<std::byte> data);
std::array<Sv2CipherState, 2> Split();

private:
Expand Down Expand Up @@ -210,6 +217,28 @@ class Sv2HandshakeState
[[nodiscard]] bool GenerateEvenYCoordinateKey(CKey& key);
};

class Sv2Cipher
{
public:
bool m_initiator;

Sv2Cipher(CKey&& static_key, bool initiator);

/** TODO: unused after HANDSHAKE state, so clear/remove (std::optional?) */
Sv2HandshakeState m_handshake_state;

SessionState m_session_state;

uint256 m_hash;
Sv2CipherState m_cs1;
Sv2CipherState m_cs2;

bool DecryptMessage(Span<std::byte> message);
void EncryptMessage(Span<std::byte> input, Span<std::byte> output);
void FinishHandshake();
};

/** TODO: delete class, have Sv2Transport manage session */
// NoiseSession encapsulates the whole handshake state and subsequent secure
// communication.
class Sv2NoiseSession
Expand Down Expand Up @@ -240,8 +269,9 @@ class Sv2NoiseSession
*
* @param[in] message message to be decrypted
*
* @returns whether decryption succeeded
*/
void DecryptMessage(Span<std::byte> message);
[[ nodiscard ]] bool DecryptMessage(Span<std::byte> message);
const uint256& GetSymmetricStateHash() const;
const SessionState& GetSessionState() const;
bool HandshakeComplete() const
Expand Down
Loading

0 comments on commit 02bc9fd

Please sign in to comment.