/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 | 0 | { | 
| 29 |  |     // Test that BIP324Cipher's encryption and decryption agree. | 
| 30 |  |  | 
| 31 |  |     // Load keys from fuzzer. | 
| 32 | 0 |     FuzzedDataProvider provider(buffer.data(), buffer.size()); | 
| 33 |  |     // Initiator key | 
| 34 | 0 |     CKey init_key = ConsumePrivateKey(provider, /*compressed=*/true); | 
| 35 | 0 |     if (!init_key.IsValid()) return; | 
| 36 |  |     // Initiator entropy | 
| 37 | 0 |     auto init_ent = provider.ConsumeBytes<std::byte>(32); | 
| 38 | 0 |     init_ent.resize(32); | 
| 39 |  |     // Responder key | 
| 40 | 0 |     CKey resp_key = ConsumePrivateKey(provider, /*compressed=*/true); | 
| 41 | 0 |     if (!resp_key.IsValid()) return; | 
| 42 |  |     // Responder entropy | 
| 43 | 0 |     auto resp_ent = provider.ConsumeBytes<std::byte>(32); | 
| 44 | 0 |     resp_ent.resize(32); | 
| 45 |  |  | 
| 46 |  |     // Initialize ciphers by exchanging public keys. | 
| 47 | 0 |     BIP324Cipher initiator(init_key, init_ent); | 
| 48 | 0 |     assert(!initiator); | 
| 49 | 0 |     BIP324Cipher responder(resp_key, resp_ent); | 
| 50 | 0 |     assert(!responder); | 
| 51 | 0 |     initiator.Initialize(responder.GetOurPubKey(), true); | 
| 52 | 0 |     assert(initiator); | 
| 53 | 0 |     responder.Initialize(initiator.GetOurPubKey(), false); | 
| 54 | 0 |     assert(responder); | 
| 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 | 0 |     InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>()); | 
| 61 |  |  | 
| 62 |  |     // Compare session IDs and garbage terminators. | 
| 63 | 0 |     assert(std::ranges::equal(initiator.GetSessionID(), responder.GetSessionID())); | 
| 64 | 0 |     assert(std::ranges::equal(initiator.GetSendGarbageTerminator(), responder.GetReceiveGarbageTerminator())); | 
| 65 | 0 |     assert(std::ranges::equal(initiator.GetReceiveGarbageTerminator(), responder.GetSendGarbageTerminator())); | 
| 66 |  |  | 
| 67 | 0 |     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 | 0 |         unsigned mode = provider.ConsumeIntegral<uint8_t>(); | 
| 75 | 0 |         bool ignore = mode & 1; | 
| 76 | 0 |         bool from_init = mode & 2; | 
| 77 | 0 |         bool damage = mode & 4; | 
| 78 | 0 |         unsigned aad_length_bits = 4 * ((mode >> 3) & 3); | 
| 79 | 0 |         unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1); | 
| 80 | 0 |         unsigned length_bits = 2 * ((mode >> 5) & 7); | 
| 81 | 0 |         unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1); | 
| 82 |  |         // Generate aad and content. | 
| 83 | 0 |         auto aad = rng.randbytes<std::byte>(aad_length); | 
| 84 | 0 |         auto contents = rng.randbytes<std::byte>(length); | 
| 85 |  |  | 
| 86 |  |         // Pick sides. | 
| 87 | 0 |         auto& sender{from_init ? initiator : responder}; | 
| 88 | 0 |         auto& receiver{from_init ? responder : initiator}; | 
| 89 |  |  | 
| 90 |  |         // Encrypt | 
| 91 | 0 |         std::vector<std::byte> ciphertext(length + initiator.EXPANSION); | 
| 92 | 0 |         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 | 0 |         if (damage) { | 
| 97 | 0 |             unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0, | 
| 98 | 0 |                 (ciphertext.size() + aad.size()) * 8U - 1U); | 
| 99 | 0 |             unsigned damage_pos = damage_bit >> 3; | 
| 100 | 0 |             std::byte damage_val{(uint8_t)(1U << (damage_bit & 7))}; | 
| 101 | 0 |             if (damage_pos >= ciphertext.size()) { | 
| 102 | 0 |                 aad[damage_pos - ciphertext.size()] ^= damage_val; | 
| 103 | 0 |             } else { | 
| 104 | 0 |                 ciphertext[damage_pos] ^= damage_val; | 
| 105 | 0 |             } | 
| 106 | 0 |         } | 
| 107 |  |  | 
| 108 |  |         // Decrypt length | 
| 109 | 0 |         uint32_t dec_length = receiver.DecryptLength(std::span{ciphertext}.first(initiator.LENGTH_LEN)); | 
| 110 | 0 |         if (!damage) { | 
| 111 | 0 |             assert(dec_length == length); | 
| 112 | 0 |         } else { | 
| 113 |  |             // For performance reasons, don't try to decode if length got increased too much. | 
| 114 | 0 |             if (dec_length > 16384 + length) break; | 
| 115 |  |             // Otherwise, just append zeros if dec_length > length. | 
| 116 | 0 |             ciphertext.resize(dec_length + initiator.EXPANSION); | 
| 117 | 0 |         } | 
| 118 |  |  | 
| 119 |  |         // Decrypt | 
| 120 | 0 |         std::vector<std::byte> decrypt(dec_length); | 
| 121 | 0 |         bool dec_ignore{false}; | 
| 122 | 0 |         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 | 0 |         assert(!ok == damage); | 
| 125 | 0 |         if (!ok) break; | 
| 126 | 0 |         assert(ignore == dec_ignore); | 
| 127 | 0 |         assert(decrypt == contents); | 
| 128 | 0 |     } | 
| 129 | 0 | } |