From ad67fd2e0bfa6f43f350066596b6cca146391362 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 1 Dec 2021 14:19:08 -0500 Subject: [PATCH] Add a fuzz test for Num3072 multiplication and inversion --- src/arith_uint256.cpp | 4 + src/test/fuzz/muhash.cpp | 156 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) diff --git a/src/arith_uint256.cpp b/src/arith_uint256.cpp index 0d5b3d5b0e..850fc624b8 100644 --- a/src/arith_uint256.cpp +++ b/src/arith_uint256.cpp @@ -229,3 +229,7 @@ arith_uint256 UintToArith256(const uint256 &a) b.pn[x] = ReadLE32(a.begin() + x*4); return b; } + +// Explicit instantiations for base_uint<6144> (used in test/fuzz/muhash.cpp). +template base_uint<6144>& base_uint<6144>::operator*=(const base_uint<6144>& b); +template base_uint<6144>& base_uint<6144>::operator/=(const base_uint<6144>& b); diff --git a/src/test/fuzz/muhash.cpp b/src/test/fuzz/muhash.cpp index e74bcb962f..f1dd5ebaf9 100644 --- a/src/test/fuzz/muhash.cpp +++ b/src/test/fuzz/muhash.cpp @@ -2,13 +2,169 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include #include +#include +#include #include #include #include +#include +#include #include +namespace { + +/** Class to represent 6144-bit numbers using arith_uint256 code. + * + * 6144 is sufficient to represent the product of two 3072-bit numbers. */ +class arith_uint6144 : public base_uint<6144> { +public: + arith_uint6144(uint64_t x) : base_uint{x} {} + + /** Construct an arith_uint6144 from any multiple of 4 bytes in LE notation, + * up to 768 bytes. */ + arith_uint6144(Span bytes) : base_uint{} + { + assert(bytes.size() % 4 == 0); + assert(bytes.size() <= 768); + for (unsigned i = 0; i * 4 < bytes.size(); ++i) { + pn[i] = ReadLE32(bytes.data() + 4 * i); + } + } + + /** Serialize an arithm_uint6144 to any multiply of 4 bytes in LE notation, + * on the condition that the represented number fits. */ + void Serialize(Span bytes) { + assert(bytes.size() % 4 == 0); + assert(bytes.size() <= 768); + for (unsigned i = 0; i * 4 < bytes.size(); ++i) { + WriteLE32(bytes.data() + 4 * i, pn[i]); + } + for (unsigned i = bytes.size() / 4; i * 4 < 768; ++i) { + assert(pn[i] == 0); + } + }; +}; + +/** The MuHash3072 modulus (2**3072 - 1103717) as 768 LE8 bytes. */ +constexpr std::array MODULUS_BYTES = { + 155, 40, 239, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +const arith_uint6144 ZERO{0}; +const arith_uint6144 ONE{1}; +const arith_uint6144 MODULUS{MODULUS_BYTES}; + +/** Update value to be the modulus of the input modulo MODULUS. */ +void Reduce(arith_uint6144& value) +{ + arith_uint6144 tmp = value; + tmp /= MODULUS; + tmp *= MODULUS; + value -= tmp; +} + +} // namespace + +FUZZ_TARGET(num3072_mul) +{ + // Test multiplication + FuzzedDataProvider provider{buffer.data(), buffer.size()}; + + // Read two 3072-bit numbers from fuzz input, and construct arith_uint6144 + // and Num3072 objects with the read values. + uint16_t data_a_len = provider.ConsumeIntegralInRange(0, 384); + uint8_t data_a[384] = {0}; + provider.ConsumeData(data_a, data_a_len); + arith_uint6144 a_uint{data_a}; + Num3072 a_num{data_a}; + + uint8_t data_b[384] = {0}; + provider.ConsumeData(data_b, 384); + arith_uint6144 b_uint{data_b}; + Num3072 b_num{data_b}; + + // Multiply the first number with the second, in both representations. + a_num.Multiply(b_num); + a_uint *= b_uint; + Reduce(a_uint); + + // Serialize both to bytes and compare. + uint8_t buf_num[384], buf_uint[384]; + a_num.ToBytes(buf_num); + a_uint.Serialize(buf_uint); + assert(std::ranges::equal(buf_num, buf_uint)); +} + +FUZZ_TARGET(num3072_inv) +{ + // Test inversion + + FuzzedDataProvider provider{buffer.data(), buffer.size()}; + + // Read a 3072-bit number from fuzz input, and construct arith_uint6144 + // and Num3072 objects with the read values. + uint8_t data[384] = {0}; + provider.ConsumeData(data, 384); + Num3072 num{data}; + arith_uint6144 uint{data}; + + // Bail out if the number has no inverse. + if ((uint == ZERO) || (uint == MODULUS)) return; + + // Compute the inverse of the Num3072 object. + Num3072 inv; + inv.SetToOne(); + inv.Divide(num); + + // Convert the computed inverse to arith_uint6144. + uint8_t buf[384]; + inv.ToBytes(buf); + arith_uint6144 uint_inv{buf}; + + // Multiply the original and the inverse, and expect 1. + uint *= uint_inv; + Reduce(uint); + assert(uint == ONE); +} + FUZZ_TARGET(muhash) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};