libdpf/include/dpf/constrained_cmp.hpp
Ryan Henry 0d22946a0e Checkpoint the party/runtime stack before share-program and malicious-mode work.
Ship the TLS mesh, composer, Beaver/Yao/leaf MPC, prep/online paths, apps, and docs so the tree is pushable before elevating share_expr, security_mode, and prep resume.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-28 05:59:19 -06:00

113 lines
3.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/// @file dpf/constrained_cmp.hpp
/// @brief Constrained integer comparison Π_CCMP (NDSS 2025 Alg. 1).
/// @details Given two positive integers that differ by exactly one,
/// `1{x0 < x1}` is computed with a single AND on two derived bits.
/// Local joint simulation opens the AND clearly; an MPC backend would
/// replace that open with the existing Beaver AND tape.
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
/// see [LICENSE.md](@ref license) for details.
#ifndef LIBDPF_INCLUDE_DPF_CONSTRAINED_CMP_HPP__
#define LIBDPF_INCLUDE_DPF_CONSTRAINED_CMP_HPP__
#include <cstdint>
#include <stdexcept>
#include <type_traits>
#include "hedley/hedley.h"
namespace dpf
{
namespace detail
{
/// @brief Last two bits of `x`: high = bit 1, low = bit 0.
/// @param x the `x`
/// @return Last two bits of `x`: high = bit 1, low = bit 0
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_CONST
constexpr uint8_t ccmp_lo_bit(std::uint64_t x) noexcept
{
return static_cast<uint8_t>(x & 1u);
}
/// @brief Bit 1 of `x`.
/// @param x the integer
/// @return `(x >> 1) & 1`
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_CONST
constexpr uint8_t ccmp_hi_bit(std::uint64_t x) noexcept
{
return static_cast<uint8_t>((x >> 1) & 1u);
}
/// @brief Party `b`'s local share inputs for the AND: `z0 = h`, `z1 = h ⊕ l ⊕ b`.
/// @param x the integer whose low two bits are split
/// @param party party index, `0` or `1`
/// @param z0 first AND input, `h`
/// @param z1 second AND input, `h ⊕ l ⊕ party`
/// @param l bit 0 of `x`
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr void ccmp_party_terms(std::uint64_t x, uint8_t party,
uint8_t & z0, uint8_t & z1, uint8_t & l) noexcept
{
const uint8_t h = ccmp_hi_bit(x);
l = ccmp_lo_bit(x);
z0 = h;
z1 = static_cast<uint8_t>(h ^ l ^ (party & 1u));
}
/// @brief Opened result of Π_CCMP when both inputs are known (local joint sim).
/// @details Aborts unless `|x0 - x1| = 1`.
/// @param x0 the `x0`
/// @param x1 the `x1`
/// @return Opened result of Π_CCMP when both inputs are known (local joint sim)
/// @throws std::invalid_argument when the inputs do not differ by one
HEDLEY_ALWAYS_INLINE
uint8_t local_ccmp(std::uint64_t x0, std::uint64_t x1)
{
const std::uint64_t diff = x0 > x1 ? x0 - x1 : x1 - x0;
if (diff != 1ULL)
throw std::invalid_argument(
"constrained comparison: inputs must differ by exactly one");
uint8_t z00 = 0, z01 = 0, l0 = 0;
uint8_t z10 = 0, z11 = 0, l1 = 0;
ccmp_party_terms(x0, 0, z00, z01, l0);
ccmp_party_terms(x1, 1, z10, z11, l1);
const uint8_t z0 = static_cast<uint8_t>(z00 ^ z10);
const uint8_t z1 = static_cast<uint8_t>(z01 ^ z11);
const uint8_t t = static_cast<uint8_t>(z0 & z1);
// Party shares: y0 = t0, y1 = t1 ⊕ (l1 ∧ 1). Opened y = t ⊕ l1.
return static_cast<uint8_t>(t ^ l1);
}
/// @brief Same as `local_ccmp` for any unsigned or enum-convertible integer.
/// @tparam T0 integral type of the first operand
/// @tparam T1 integral type of the second operand
/// @param x0 the first integer
/// @param x1 the second integer
/// @return Same as `local_ccmp` for any unsigned or enum-convertible integer
/// @throws std::invalid_argument when the inputs do not differ by one
template <typename T0, typename T1>
HEDLEY_ALWAYS_INLINE
uint8_t local_ccmp_int(T0 x0, T1 x1)
{
static_assert(std::is_integral_v<T0> && std::is_integral_v<T1>,
"local_ccmp_int: integral operands");
return local_ccmp(static_cast<std::uint64_t>(x0),
static_cast<std::uint64_t>(x1));
}
} // namespace detail
/// @brief Constrained comparison: `1{x0 < x1}` when `|x0 − x1| = 1`.
using detail::local_ccmp;
using detail::local_ccmp_int;
} // namespace dpf
#endif // LIBDPF_INCLUDE_DPF_CONSTRAINED_CMP_HPP__