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