Skip to content

Commit 6d0caf4

Browse files
committed
fuzz: Add fuzz test for v2 transport {de}serialization
1 parent b3f6d71 commit 6d0caf4

File tree

4 files changed

+121
-1
lines changed

4 files changed

+121
-1
lines changed

src/Makefile.test.include

+1
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ test_fuzz_fuzz_SOURCES = \
293293
test/fuzz/netbase_dns_lookup.cpp \
294294
test/fuzz/node_eviction.cpp \
295295
test/fuzz/p2p_transport_serialization.cpp \
296+
test/fuzz/p2p_v2_transport_serialization.cpp \
296297
test/fuzz/parse_hd_keypath.cpp \
297298
test/fuzz/parse_numbers.cpp \
298299
test/fuzz/parse_script.cpp \

src/crypto/rfc8439.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
#include <crypto/rfc8439.h>
66

7-
#include <crypto/chacha20.h>
87
#include <crypto/common.h>
98

109
#include <cstring>

src/crypto/rfc8439.h

+8
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#ifndef BITCOIN_CRYPTO_RFC8439_H
66
#define BITCOIN_CRYPTO_RFC8439_H
77

8+
#include <crypto/chacha20.h>
89
#include <crypto/poly1305.h>
910
#include <span.h>
1011

@@ -19,4 +20,11 @@ void RFC8439Encrypt(const Span<const std::byte> aad, const Span<const std::byte>
1920

2021
// returns false if authentication fails
2122
bool RFC8439Decrypt(const Span<const std::byte> aad, const Span<const std::byte> key, const std::array<std::byte, 12>& nonce, const Span<const std::byte> input, Span<std::byte> plaintext);
23+
24+
void ComputeRFC8439Tag(const std::array<std::byte, POLY1305_KEYLEN>& polykey,
25+
Span<const std::byte> aad, Span<const std::byte> ciphertext,
26+
Span<std::byte> tag_out);
27+
28+
std::array<std::byte, POLY1305_KEYLEN> GetPoly1305Key(ChaCha20& c20);
29+
2230
#endif // BITCOIN_CRYPTO_RFC8439_H
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Copyright (c) 2019-2021 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <compat/endian.h>
6+
#include <crypto/bip324_suite.h>
7+
#include <crypto/rfc8439.h>
8+
#include <key.h>
9+
#include <net.h>
10+
#include <netmessagemaker.h>
11+
#include <test/fuzz/FuzzedDataProvider.h>
12+
#include <test/fuzz/fuzz.h>
13+
14+
#include <array>
15+
#include <cassert>
16+
#include <cstddef>
17+
18+
FUZZ_TARGET(p2p_v2_transport_serialization)
19+
{
20+
FuzzedDataProvider fdp{buffer.data(), buffer.size()};
21+
22+
// Picking constant keys seems to give us higher fuzz test coverage
23+
// The BIP324 Cipher suite is separately fuzzed, so we don't have to
24+
// pick fuzzed keys here.
25+
BIP324Key key_L, key_P;
26+
std::array<std::byte, BIP324_REKEY_SALT_LEN> rekey_salt;
27+
28+
memset(key_L.data(), 1, BIP324_KEY_LEN);
29+
memset(key_P.data(), 2, BIP324_KEY_LEN);
30+
memset(rekey_salt.data(), 3, BIP324_REKEY_SALT_LEN);
31+
32+
// Construct deserializer, with a dummy NodeId
33+
V2TransportDeserializer deserializer{(NodeId)0, key_L, key_P, rekey_salt};
34+
V2TransportSerializer serializer{key_L, key_P, rekey_salt};
35+
FSChaCha20 fsc20{key_L, rekey_salt, REKEY_INTERVAL};
36+
ChaCha20 c20{reinterpret_cast<unsigned char*>(key_P.data())};
37+
38+
std::array<std::byte, 12> nonce;
39+
memset(nonce.data(), 0, 12);
40+
c20.SetRFC8439Nonce(nonce);
41+
42+
bool length_assist = fdp.ConsumeBool();
43+
44+
// There is no sense in providing a mac assist if the length is incorrect.
45+
bool mac_assist = length_assist && fdp.ConsumeBool();
46+
auto encrypted_packet = fdp.ConsumeRemainingBytes<uint8_t>();
47+
bool is_decoy_packet{false};
48+
49+
if (encrypted_packet.size() >= V2_MIN_PACKET_LENGTH) {
50+
if (length_assist) {
51+
uint32_t contents_len = encrypted_packet.size() - BIP324_LENGTH_FIELD_LEN - BIP324_HEADER_LEN - RFC8439_EXPANSION;
52+
contents_len = htole32(contents_len);
53+
fsc20.Crypt({reinterpret_cast<std::byte*>(&contents_len), BIP324_LENGTH_FIELD_LEN},
54+
{reinterpret_cast<std::byte*>(encrypted_packet.data()), BIP324_LENGTH_FIELD_LEN});
55+
}
56+
57+
if (mac_assist) {
58+
std::array<std::byte, RFC8439_EXPANSION> tag;
59+
ComputeRFC8439Tag(GetPoly1305Key(c20), {},
60+
{reinterpret_cast<std::byte*>(encrypted_packet.data()) + BIP324_LENGTH_FIELD_LEN,
61+
encrypted_packet.size() - BIP324_LENGTH_FIELD_LEN - RFC8439_EXPANSION}, tag);
62+
memcpy(encrypted_packet.data() + encrypted_packet.size() - RFC8439_EXPANSION, tag.data(), RFC8439_EXPANSION);
63+
64+
std::vector<std::byte> dec_header_and_contents(
65+
encrypted_packet.size() - BIP324_LENGTH_FIELD_LEN - RFC8439_EXPANSION);
66+
RFC8439Decrypt({}, key_P, nonce,
67+
{reinterpret_cast<std::byte*>(encrypted_packet.data() + BIP324_LENGTH_FIELD_LEN),
68+
encrypted_packet.size() - BIP324_LENGTH_FIELD_LEN},
69+
dec_header_and_contents);
70+
if (BIP324HeaderFlags((uint8_t)dec_header_and_contents.at(0) & BIP324_IGNORE) != BIP324_NONE) {
71+
is_decoy_packet = true;
72+
}
73+
}
74+
}
75+
76+
Span<const uint8_t> pkt_bytes{encrypted_packet};
77+
while (pkt_bytes.size() > 0) {
78+
const int handled = deserializer.Read(pkt_bytes);
79+
if (handled < 0) {
80+
break;
81+
}
82+
if (deserializer.Complete()) {
83+
const std::chrono::microseconds m_time{std::numeric_limits<int64_t>::max()};
84+
bool reject_message{true};
85+
bool disconnect{true};
86+
CNetMessage result{deserializer.GetMessage(m_time, reject_message, disconnect)};
87+
88+
if (mac_assist) {
89+
assert(!disconnect);
90+
}
91+
92+
if (is_decoy_packet) {
93+
assert(reject_message);
94+
}
95+
96+
if (!reject_message) {
97+
assert(result.m_type.size() <= CMessageHeader::COMMAND_SIZE);
98+
assert(result.m_raw_message_size <= buffer.size());
99+
100+
auto message_type_size = result.m_raw_message_size - V2_MIN_PACKET_LENGTH - result.m_message_size;
101+
assert(message_type_size <= 13);
102+
assert(message_type_size >= 1);
103+
assert(result.m_time == m_time);
104+
105+
std::vector<unsigned char> header;
106+
auto msg = CNetMsgMaker{result.m_recv.GetVersion()}.Make(result.m_type, MakeUCharSpan(result.m_recv));
107+
// if decryption succeeds, encryption must succeed
108+
assert(serializer.prepareForTransport(msg, header));
109+
}
110+
}
111+
}
112+
}

0 commit comments

Comments
 (0)