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