mta - Multiplicative-to-Additive Conversion

Overview

The mta module implements Multiplicative-to-Additive (MtA) share conversion, a fundamental building block for threshold ECDSA protocols. MtA enables two parties holding multiplicative shares (a, b) to convert them into additive shares (α, β) such that a·b = α + β (mod q), without revealing their individual shares.

This implementation is based on the DKLS23 paper (“Threshold ECDSA from ECDSA Assumptions: The Multiparty Case”) and uses real oblivious transfer (OT) for security rather than simulation.

Key Features

  • Secure Share Conversion: Converts multiplicative to additive shares without revealing inputs

  • OT-Based Security: Uses real SimpleOT for cryptographic security guarantees

  • MtA with Check (MtAwc): Includes zero-knowledge proofs for malicious security

  • Curve Agnostic: Works with any DDH-hard elliptic curve group

  • Bit-Level OT: Uses correlated OT with one OT per bit of the secret

Security Properties

  • Sender Privacy: Receiver learns nothing about sender’s share beyond the additive output

  • Receiver Privacy: Sender learns nothing about receiver’s share

  • Correctness: The additive shares always sum to the original product

  • Malicious Security (MtAwc): Zero-knowledge proofs detect cheating parties

Use Cases

  • DKLS23 Presigning: Core component for generating presignatures

  • Threshold ECDSA: Enables secure multiplication of secret shares

  • Two-Party Computation: General-purpose secure multiplication protocol

Example Usage

Basic MtA Conversion:

from charm.toolbox.ecgroup import ECGroup, ZR
from charm.toolbox.eccurve import secp256k1
from charm.toolbox.mta import MtA

group = ECGroup(secp256k1)

# Create separate MtA instances for Alice and Bob
alice_mta = MtA(group)
bob_mta = MtA(group)

# Alice has share a, Bob has share b
a = group.random(ZR)
b = group.random(ZR)

# Run the 4-round MtA protocol
sender_msg = alice_mta.sender_round1(a)
receiver_msg, _ = bob_mta.receiver_round1(b, sender_msg)
alpha, ot_data = alice_mta.sender_round2(receiver_msg)
beta = bob_mta.receiver_round2(ot_data)

# Verify: a*b = alpha + beta (mod q)
assert a * b == alpha + beta

MtA with Correctness Check (MtAwc):

from charm.toolbox.mta import MtAwc

mta_wc = MtAwc(group)

a = group.random(ZR)
b = group.random(ZR)

# Commitment phase
sender_commit = mta_wc.sender_commit(a)
receiver_commit = mta_wc.receiver_commit(b)

# MtA with ZK proofs
sender_msg = mta_wc.sender_round1(a, receiver_commit)
receiver_msg, _ = mta_wc.receiver_round1(b, sender_commit, sender_msg)
alpha, proof = mta_wc.sender_round2(receiver_msg)
beta, valid = mta_wc.receiver_verify(proof)

assert valid  # Proof verified
assert a * b == alpha + beta

Protocol Details

The MtA protocol works as follows:

  1. Sender Setup: Sender decomposes share a into bits and creates OT sender instances

  2. Receiver Choose: Receiver uses bits of b to select OT messages (learns only selected values)

  3. Sender Transfer: Sender encrypts correlated messages via OT

  4. Receiver Output: Receiver decrypts selected messages and computes additive share

For each bit position i, the correlation is: m₁ = m₀ + a·2ⁱ, ensuring the sum of selected values equals a·b.

API Reference

Multiplicative-to-Additive (MtA) Share Conversion for DKLS23

From: “Threshold ECDSA from ECDSA Assumptions: The Multiparty Case”
By: Jack Doerner, Yashvanth Kondi, Eysa Lee, abhi shelat
Published: IEEE S&P 2019

Also implements MtAwc (MtA with check) from:
“Two-Round Threshold ECDSA from ECDSA Assumptions” (DKLS23)
By: Jack Doerner, Yashvanth Kondi, Eysa Lee, abhi shelat
Published: IEEE S&P 2023
  • type: share conversion

  • setting: Elliptic Curve DDH-hard group

  • assumption: DDH + OT security

MtA converts multiplicative shares (a, b) where two parties hold a and b to additive shares (alpha, beta) such that a*b = alpha + beta (mod q). Neither party learns the other’s share.

Authors:

Elton de Souza

Date:

01/2026

class mta.CorrelatedOT(groupObj)[source]

Bases: object

Correlated Oblivious Transfer for MtA.

Generates correlated random values for OT-based MtA. For each bit of the sender’s input, generates correlation pairs.

generate_batch_correlations(deltas)[source]

Generate batch of correlated pairs.

Parameters

deltaslist of int

List of correlation offsets

Returns

list of tuples

List of (t0, t1) pairs

generate_correlation(delta)[source]

Generate correlated pair (t0, t1) where t1 = t0 + delta.

Parameters

deltaint

The correlation offset

Returns

tuple

(t0, t1) where t1 = t0 + delta (mod order)

class mta.MtA(groupObj: Any)[source]

Bases: object

Multiplicative-to-Additive share conversion using OT.

Converts multiplicative shares (a, b) where parties hold a and b to additive shares (alpha, beta) where a*b = alpha + beta (mod q).

Curve Agnostic

This implementation supports any elliptic curve group that is DDH-hard. The curve is specified via the groupObj parameter.

The protocol works as follows: 1. Sender (holding a) decomposes a into bits 2. For each bit position i, run correlated OT with correlation 2^i * b 3. Receiver (holding b) chooses based on sender’s bits 4. Parties compute their additive shares from OT outputs

>>> from charm.toolbox.eccurve import secp256k1
>>> group = ECGroup(secp256k1)
>>> # Create separate instances for Alice (sender) and Bob (receiver)
>>> alice_mta = MtA(group)
>>> bob_mta = MtA(group)
>>> # Alice has share a, Bob has share b
>>> a = group.random(ZR)
>>> b = group.random(ZR)
>>> # Convert to additive shares using the protocol with real OT
>>> sender_msg = alice_mta.sender_round1(a)
>>> receiver_msg, _ = bob_mta.receiver_round1(b, sender_msg)
>>> alpha, ot_data = alice_mta.sender_round2(receiver_msg)
>>> beta = bob_mta.receiver_round2(ot_data)
>>> # Verify: a*b = alpha + beta (mod q)
>>> product = a * b
>>> additive_sum = alpha + beta
>>> product == additive_sum
True
receiver_complete(sender_bits: List[int]) Any[source]

Receiver returns their additive share beta (already computed).

Parameters

sender_bitslist of int

Sender’s bit decomposition (unused in correct protocol)

Returns

ZR element

Receiver’s additive share beta

receiver_round1(b: Any, sender_msg: Dict[str, Any]) Tuple[Dict[str, Any], None][source]

Receiver (holding b) selects OT messages based on bits of b.

Uses real SimpleOT: for each bit b_i, receiver only learns m_{b_i}. The receiver NEVER sees both m0 and m1.

Parameters

bZR element

Receiver’s multiplicative share

sender_msgdict

Message from sender_round1

Returns

tuple (dict, None)

A tuple containing: - dict: Receiver parameters with ‘ot_responses’ list of OT receiver responses - None: Placeholder for beta (computed in receiver_round2)

receiver_round2(sender_round2_msg: Dict[str, Any]) Any[source]

Receiver retrieves selected OT messages and computes beta.

Parameters

sender_round2_msgdict

Message from sender_round2 containing OT ciphertexts

Returns

ZR element

Receiver’s additive share beta such that a*b = alpha + beta (mod q)

sender_round1(a: Any) Dict[str, Any][source]

Sender (holding a) generates first message.

Sender samples random alpha and prepares OT messages such that receiver can learn beta = a*b - alpha. Uses real SimpleOT for security.

Parameters

aZR element

Sender’s multiplicative share

Returns

dict

OT setup parameters containing: - ‘ot_params’: list of OT sender parameters (one per bit position) - ‘adjustment’: integer for receiver to compute beta

sender_round2(receiver_msg: Dict[str, Any]) Tuple[Any, Dict[str, Any]][source]

Sender processes receiver’s OT responses and returns alpha.

Parameters

receiver_msgdict

Message from receiver_round1 containing OT responses

Returns

tuple (ZR element, dict)

A tuple containing: - ZR element: Sender’s additive share alpha - dict: OT data with ‘ot_ciphertexts’ list for receiver to retrieve

class mta.MtAwc(groupObj: Any)[source]

Bases: object

MtA with check - includes ZK proof that conversion is correct.

Used for malicious security. Adds commitment and proof phases to verify that parties performed MtA correctly.

The protocol adds: 1. Commitment phase: parties commit to their shares 2. Proof phase: parties prove correctness of OT selections 3. Verification: parties verify each other’s proofs

>>> from charm.toolbox.eccurve import secp256k1
>>> group = ECGroup(secp256k1)
>>> mta_wc = MtAwc(group)
>>> # Alice has share a, Bob has share b
>>> a = group.random(ZR)
>>> b = group.random(ZR)
>>> # Run MtA with correctness check
>>> sender_commit = mta_wc.sender_commit(a)
>>> receiver_commit = mta_wc.receiver_commit(b)
>>> # Exchange commitments and run MtA
>>> sender_msg = mta_wc.sender_round1(a, receiver_commit)
>>> receiver_msg, _ = mta_wc.receiver_round1(b, sender_commit, sender_msg)
>>> alpha, sender_proof = mta_wc.sender_round2(receiver_msg)
>>> beta, valid = mta_wc.receiver_verify(sender_proof)
>>> valid
True
>>> # Verify: a*b = alpha + beta (mod q)
>>> product = a * b
>>> additive_sum = alpha + beta
>>> product == additive_sum
True
receiver_commit(b: Any) Dict[str, Any][source]

Receiver commits to share b.

Parameters

bZR element

Receiver’s multiplicative share

Returns

dict

Commitment to send to sender

receiver_round1(b: Any, sender_commit: Dict[str, Any], sender_msg: Dict[str, Any]) Tuple[Dict[str, Any], None][source]

Receiver processes sender message with commitments.

Parameters

bZR element

Receiver’s multiplicative share

sender_commitdict

Sender’s commitment from sender_commit (includes bit_proof)

sender_msgdict

Message from sender_round1

Returns

tuple

(receiver_message, beta_placeholder)

receiver_verify(proof: Dict[str, Any]) Tuple[Any | None, bool][source]

Receiver verifies proof including ZK bit decomposition and returns beta.

Implements full ZK verification per DKLS23 Section 3: 1. Verifies challenge-response for commitment 2. Verifies bit decomposition OR-proofs (each bit is 0 or 1) 3. Verifies bits sum to the committed value

Parameters

proofdict

Proof from sender_round2

Returns

tuple

(beta, valid) where: - beta: receiver’s additive share - valid: boolean indicating if proof is valid

sender_commit(a: Any) Dict[str, Any][source]

Sender commits to share a with ZK bit decomposition proof.

Parameters

aZR element

Sender’s multiplicative share

Returns

dict

Commitment and bit decomposition proof to send to receiver

sender_round1(a: Any, receiver_commit: Dict[str, Any]) Dict[str, Any][source]

Sender generates first message with receiver’s commitment.

Parameters

aZR element

Sender’s multiplicative share

receiver_commitdict

Receiver’s commitment from receiver_commit

Returns

dict

Message to send to receiver

sender_round2(receiver_msg: Dict[str, Any]) Tuple[Any, Dict[str, Any]][source]

Sender completes MtA and generates proof.

Parameters

receiver_msgdict

Message from receiver_round1

Returns

tuple

(alpha, proof) where: - alpha: sender’s additive share - proof: ZK proof of correctness (does NOT reveal sender_bits)

mta.hash_to_field(group: Any, *args: Any) Any[source]

Hash multiple values to a field element with domain separation.

Uses group.hash() for proper domain separation and automatic serialization of different types.

Parameters

groupECGroup

The elliptic curve group

*argsvarious

Values to hash

Returns

ZR element

Hash output as field element