Source code for mpc_utils

'''
MPC Utility Functions for Charm

Common utilities for multi-party computation protocols including:
- Byte/integer conversion with consistent big-endian ordering
- Bit decomposition and reconstruction for OT-based protocols
- Pedersen commitment scheme for hiding commitments

:Authors: Elton de Souza
:Date:    01/2026
'''

from charm.toolbox.ecgroup import ECGroup, ZR, G
from typing import List, Tuple, Any, Optional

# Type aliases
ZRElement = Any
GElement = Any
ECGroupType = Any


[docs] def int_to_bytes(n: int, length: int) -> bytes: """ Convert a non-negative integer to a fixed-length byte string. Uses big-endian byte ordering (most significant byte first), which is standard for cryptographic protocols. Parameters ---------- n : int Non-negative integer to convert. Must fit within `length` bytes. length : int Exact number of bytes in the output. Value is zero-padded if needed. Returns ------- bytes Big-endian representation of `n` with exactly `length` bytes. Raises ------ OverflowError If `n` is too large to fit in `length` bytes. ValueError If `n` is negative. Examples -------- >>> int_to_bytes(256, 2) b'\\x01\\x00' >>> int_to_bytes(0, 4) b'\\x00\\x00\\x00\\x00' """ if n < 0: raise ValueError("n must be non-negative") return n.to_bytes(length, byteorder='big')
[docs] def bytes_to_int(b: bytes) -> int: """ Convert a byte string to a non-negative integer. Uses big-endian byte ordering (most significant byte first), which is standard for cryptographic protocols. Parameters ---------- b : bytes Byte string to convert. Returns ------- int The integer value represented by the bytes. Examples -------- >>> bytes_to_int(b'\\x01\\x00') 256 >>> bytes_to_int(b'\\x00\\x00\\x00\\x00') 0 """ return int.from_bytes(b, byteorder='big')
[docs] def bit_decompose(value: Any, order: int, num_bits: int) -> List[int]: """ Decompose a field element into its bit representation. The input value is first reduced modulo order to ensure consistent behavior for values at or near the group order boundary. Parameters ---------- value : ZR element or int The value to decompose (will be reduced mod order) order : int The group order num_bits : int Number of bits to extract Returns ------- list of int List of bits (0 or 1), LSB first Examples -------- >>> bit_decompose(5, 2**256, 4) [1, 0, 1, 0] >>> bit_decompose(0, 2**256, 4) [0, 0, 0, 0] """ if hasattr(value, '__int__'): v = int(value) % order else: v = int(str(value)) % order bits = [] for i in range(num_bits): bits.append((v >> i) & 1) return bits
[docs] def bits_to_int(bits: List[int], order: int) -> int: """ Reconstruct an integer from its bit representation, reduced mod order. This is the inverse of bit_decompose. The result is always reduced modulo the group order to ensure values stay in the valid field range. Parameters ---------- bits : list of int List of bits (0 or 1), LSB first order : int The group order Returns ------- int The reconstructed integer, reduced mod order Examples -------- >>> bits_to_int([1, 0, 1, 0], 2**256) 5 >>> bits_to_int([0, 0, 0, 0], 2**256) 0 """ result = 0 for i, bit in enumerate(bits): if bit: result += (1 << i) return result % order
[docs] class PedersenCommitment: """ Pedersen Commitment Scheme for Elliptic Curve Groups. Implements the information-theoretically hiding commitment scheme: C = g^value * h^randomness where g and h are generators with unknown discrete log relationship. Properties: - Computationally binding (under DLP assumption) - Information-theoretically hiding - Additively homomorphic Parameters ---------- group : ECGroup An elliptic curve group object g : GElement, optional First generator (random if not provided) h : GElement, optional Second generator (random if not provided) Examples -------- >>> from charm.toolbox.eccurve import secp256k1 >>> from charm.toolbox.ecgroup import ECGroup, ZR >>> group = ECGroup(secp256k1) >>> pc = PedersenCommitment(group) >>> pc.setup() >>> value = group.random(ZR) >>> c, r = pc.commit(value) >>> pc.open(c, value, r) True """ def __init__(self, group: ECGroup, g: Optional[GElement] = None, h: Optional[GElement] = None): if group is None: raise ValueError("group cannot be None") self.group = group self.order = int(group.order()) self._g = g self._h = h
[docs] def setup(self) -> Tuple[GElement, GElement]: """Generate random generators if not already set.""" if self._g is None: self._g = self.group.random(G) if self._h is None: self._h = self.group.random(G) return self._g, self._h
@property def g(self) -> GElement: """First generator.""" if self._g is None: raise RuntimeError("Call setup() first") return self._g @property def h(self) -> GElement: """Second generator.""" if self._h is None: raise RuntimeError("Call setup() first") return self._h
[docs] def commit(self, value: Any, randomness: Optional[ZRElement] = None ) -> Tuple[GElement, ZRElement]: """ Create Pedersen commitment: C = g^value * h^randomness. Parameters ---------- value : ZRElement or int Value to commit to randomness : ZRElement, optional Randomness for commitment (generated if not provided) Returns ------- tuple (commitment, randomness) """ if randomness is None: randomness = self.group.random(ZR) if isinstance(value, int): value = self.group.init(ZR, value % self.order) commitment = (self.g ** value) * (self.h ** randomness) return commitment, randomness
[docs] def open(self, commitment: GElement, value: Any, randomness: ZRElement) -> bool: """ Verify that a commitment opens to the given value. Parameters ---------- commitment : GElement The commitment to verify value : ZRElement or int The claimed value randomness : ZRElement The randomness used in commitment Returns ------- bool True if commitment opens correctly """ if isinstance(value, int): value = self.group.init(ZR, value % self.order) expected = (self.g ** value) * (self.h ** randomness) return commitment == expected
[docs] def add(self, c1: GElement, c2: GElement) -> GElement: """ Homomorphically add two commitments. If c1 = Commit(v1, r1) and c2 = Commit(v2, r2), then c1 * c2 = Commit(v1 + v2, r1 + r2). Parameters ---------- c1 : GElement First commitment c2 : GElement Second commitment Returns ------- GElement Combined commitment """ return c1 * c2