Coverage Report

Created: 2026-08-25 19:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/bitcoin/src/test/fuzz/bip324.cpp
Line
Count
Source
1
// Copyright (c) 2023-present 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 <bip324.h>
6
#include <chainparams.h>
7
#include <random.h>
8
#include <span.h>
9
#include <test/fuzz/FuzzedDataProvider.h>
10
#include <test/fuzz/fuzz.h>
11
#include <test/fuzz/util.h>
12
13
#include <algorithm>
14
#include <cstdint>
15
#include <vector>
16
17
namespace {
18
19
void Initialize()
20
0
{
21
0
    static ECC_Context ecc_context{};
22
0
    SelectParams(ChainType::MAIN);
23
0
}
24
25
}  // namespace
26
27
FUZZ_TARGET(bip324_cipher_roundtrip, .init=Initialize)
28
1.58k
{
29
    // Test that BIP324Cipher's encryption and decryption agree.
30
31
    // Load keys from fuzzer.
32
1.58k
    FuzzedDataProvider provider(buffer.data(), buffer.size());
33
    // Initiator key
34
1.58k
    CKey init_key = ConsumePrivateKey(provider, /*compressed=*/true);
35
1.58k
    if (!init_key.IsValid()) return;
  Branch (35:9): [True: 3, False: 1.58k]
36
    // Initiator entropy
37
1.58k
    auto init_ent = provider.ConsumeBytes<std::byte>(32);
38
1.58k
    init_ent.resize(32);
39
    // Responder key
40
1.58k
    CKey resp_key = ConsumePrivateKey(provider, /*compressed=*/true);
41
1.58k
    if (!resp_key.IsValid()) return;
  Branch (41:9): [True: 17, False: 1.56k]
42
    // Responder entropy
43
1.56k
    auto resp_ent = provider.ConsumeBytes<std::byte>(32);
44
1.56k
    resp_ent.resize(32);
45
46
    // Initialize ciphers by exchanging public keys.
47
1.56k
    BIP324Cipher initiator(init_key, init_ent);
48
1.56k
    assert(!initiator);
  Branch (48:5): [True: 1.56k, False: 0]
49
1.56k
    BIP324Cipher responder(resp_key, resp_ent);
50
1.56k
    assert(!responder);
  Branch (50:5): [True: 1.56k, False: 0]
51
1.56k
    initiator.Initialize(responder.GetOurPubKey(), true);
52
1.56k
    assert(initiator);
  Branch (52:5): [True: 1.56k, False: 0]
53
1.56k
    responder.Initialize(initiator.GetOurPubKey(), false);
54
1.56k
    assert(responder);
  Branch (54:5): [True: 1.56k, False: 0]
55
56
    // Initialize RNG deterministically, to generate contents and AAD. We assume that there are no
57
    // (potentially buggy) edge cases triggered by specific values of contents/AAD, so we can avoid
58
    // reading the actual data for those from the fuzzer input (which would need large amounts of
59
    // data).
60
1.56k
    InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
61
62
    // Compare session IDs and garbage terminators.
63
1.56k
    assert(std::ranges::equal(initiator.GetSessionID(), responder.GetSessionID()));
  Branch (63:5): [True: 1.56k, False: 0]
64
1.56k
    assert(std::ranges::equal(initiator.GetSendGarbageTerminator(), responder.GetReceiveGarbageTerminator()));
  Branch (64:5): [True: 1.56k, False: 0]
65
1.56k
    assert(std::ranges::equal(initiator.GetReceiveGarbageTerminator(), responder.GetSendGarbageTerminator()));
  Branch (65:5): [True: 1.56k, False: 0]
66
67
135k
    LIMITED_WHILE (provider.remaining_bytes(), 1000) {
68
        // Mode:
69
        // - Bit 0: whether the ignore bit is set in message
70
        // - Bit 1: whether the responder (0) or initiator (1) sends
71
        // - Bit 2: whether this ciphertext will be corrupted (making it the last sent one)
72
        // - Bit 3-4: controls the maximum aad length (max 4095 bytes)
73
        // - Bit 5-7: controls the maximum content length (max 16383 bytes, for performance reasons)
74
135k
        unsigned mode = provider.ConsumeIntegral<uint8_t>();
75
135k
        bool ignore = mode & 1;
76
135k
        bool from_init = mode & 2;
77
135k
        bool damage = mode & 4;
78
135k
        unsigned aad_length_bits = 4 * ((mode >> 3) & 3);
79
135k
        unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1);
80
135k
        unsigned length_bits = 2 * ((mode >> 5) & 7);
81
135k
        unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);
82
        // Generate aad and content.
83
135k
        auto aad = rng.randbytes<std::byte>(aad_length);
84
135k
        auto contents = rng.randbytes<std::byte>(length);
85
86
        // Pick sides.
87
135k
        auto& sender{from_init ? initiator : responder};
  Branch (87:22): [True: 37.9k, False: 97.5k]
88
135k
        auto& receiver{from_init ? responder : initiator};
  Branch (88:24): [True: 37.9k, False: 97.5k]
89
90
        // Encrypt
91
135k
        std::vector<std::byte> ciphertext(length + initiator.EXPANSION);
92
135k
        sender.Encrypt(contents, aad, ignore, ciphertext);
93
94
        // Optionally damage 1 bit in either the ciphertext (corresponding to a change in transit)
95
        // or the aad (to make sure that decryption will fail if the AAD mismatches).
96
135k
        if (damage) {
  Branch (96:13): [True: 967, False: 134k]
97
967
            unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0,
98
967
                (ciphertext.size() + aad.size()) * 8U - 1U);
99
967
            unsigned damage_pos = damage_bit >> 3;
100
967
            std::byte damage_val{(uint8_t)(1U << (damage_bit & 7))};
101
967
            if (damage_pos >= ciphertext.size()) {
  Branch (101:17): [True: 174, False: 793]
102
174
                aad[damage_pos - ciphertext.size()] ^= damage_val;
103
793
            } else {
104
793
                ciphertext[damage_pos] ^= damage_val;
105
793
            }
106
967
        }
107
108
        // Decrypt length
109
135k
        uint32_t dec_length = receiver.DecryptLength(std::span{ciphertext}.first(initiator.LENGTH_LEN));
110
135k
        if (!damage) {
  Branch (110:13): [True: 134k, False: 967]
111
134k
            assert(dec_length == length);
  Branch (111:13): [True: 134k, False: 0]
112
134k
        } else {
113
            // For performance reasons, don't try to decode if length got increased too much.
114
967
            if (dec_length > 16384 + length) break;
  Branch (114:17): [True: 50, False: 917]
115
            // Otherwise, just append zeros if dec_length > length.
116
917
            ciphertext.resize(dec_length + initiator.EXPANSION);
117
917
        }
118
119
        // Decrypt
120
135k
        std::vector<std::byte> decrypt(dec_length);
121
135k
        bool dec_ignore{false};
122
135k
        bool ok = receiver.Decrypt(std::span{ciphertext}.subspan(initiator.LENGTH_LEN), aad, dec_ignore, decrypt);
123
        // Decryption *must* fail if the packet was damaged, and succeed if it wasn't.
124
135k
        assert(!ok == damage);
  Branch (124:9): [True: 135k, False: 0]
125
135k
        if (!ok) break;
  Branch (125:13): [True: 917, False: 134k]
126
135k
        assert(ignore == dec_ignore);
  Branch (126:9): [True: 134k, False: 0]
127
134k
        assert(decrypt == contents);
  Branch (127:9): [True: 134k, False: 0]
128
134k
    }
129
1.56k
}