Introduction to Threshold Signatures
Threshold signatures are cryptographic schemes where a signature can only be produced when a minimum number (threshold) of participants cooperate. This enables distributed signing authority without any single party having full control.
System Architecture Overview
Threshold Signature System ├── Key Generation (Distributed) │ ├ - Shamir's Secret Sharing │ ├ - Verifiable Secret Sharing (VSS) │ └ - Distributed Key Generation (DKG) ├── Signature Generation │ ├ - Partial Signatures │ ├ - Signature Aggregation │ └ - Threshold Verification ├── Cryptographic Schemes │ ├ - ECDSA Threshold (GG18/GG20) │ ├ - Schnorr Threshold (FROST) │ └ - BLS Threshold Signatures └── Security Features ├ - Malicious Security ├ - Identifiable Abort └ - Proactive Security
Core Implementation
1. Maven Dependencies
<properties>
<bouncycastle.version>1.78</bouncycastle.version>
<tink.version>1.12.0</tink.version>
<guava.version>33.0.0-jre</guava.version>
<netty.version>4.1.100.Final</netty.version>
</properties>
<dependencies>
<!-- Bouncy Castle for crypto primitives -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<!-- Google Tink for high-level crypto -->
<dependency>
<groupId>com.google.crypto.tink</groupId>
<artifactId>tink</artifactId>
<version>${tink.version}</version>
</dependency>
<!-- Threshold signature libraries -->
<dependency>
<groupId>com.webauthn4j</groupId>
<artifactId>webauthn4j-core</artifactId>
<version>0.21.5.RELEASE</version>
</dependency>
<!-- Multi-party computation -->
<dependency>
<groupId>org.circe</groupId>
<artifactId>circe-mpc</artifactId>
<version>0.1.0</version>
</dependency>
<!-- Networking for distributed protocols -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>${netty.version}</version>
</dependency>
<!-- Serialization -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.1</version>
</dependency>
<!-- Utilities -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.14.0</version>
</dependency>
<!-- Zero-knowledge proofs -->
<dependency>
<groupId>com.zkproofs</groupId>
<artifactId>bulletproofs-java</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
2. Shamir's Secret Sharing Implementation
package com.threshold.crypto;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.*;
/**
* Implements Shamir's Secret Sharing over prime fields
*/
public class ShamirSecretSharing {
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
/**
* Split a secret into n shares with threshold t
*/
public static List<Share> split(BigInteger secret, int totalShares, int threshold,
BigInteger prime) {
if (threshold > totalShares) {
throw new IllegalArgumentException("Threshold cannot exceed total shares");
}
// Generate random coefficients for polynomial of degree threshold-1
List<BigInteger> coefficients = new ArrayList<>();
coefficients.add(secret); // a0 = secret
for (int i = 1; i < threshold; i++) {
BigInteger coeff = generateRandomModPrime(prime);
coefficients.add(coeff);
}
// Generate shares: f(x) for x = 1..totalShares
List<Share> shares = new ArrayList<>();
for (int i = 1; i <= totalShares; i++) {
BigInteger x = BigInteger.valueOf(i);
BigInteger y = evaluatePolynomial(coefficients, x, prime);
shares.add(new Share(i, y));
}
return shares;
}
/**
* Reconstruct secret from shares using Lagrange interpolation
*/
public static BigInteger reconstruct(List<Share> shares, BigInteger prime) {
if (shares.size() < 1) {
throw new IllegalArgumentException("Need at least 1 share");
}
// Lagrange interpolation at x=0
BigInteger secret = BigInteger.ZERO;
for (int i = 0; i < shares.size(); i++) {
Share shareI = shares.get(i);
BigInteger xi = BigInteger.valueOf(shareI.x);
BigInteger yi = shareI.y;
// Calculate Lagrange basis polynomial L_i(0)
BigInteger numerator = BigInteger.ONE;
BigInteger denominator = BigInteger.ONE;
for (int j = 0; j < shares.size(); j++) {
if (i != j) {
Share shareJ = shares.get(j);
BigInteger xj = BigInteger.valueOf(shareJ.x);
numerator = numerator.multiply(xj.negate()).mod(prime);
denominator = denominator.multiply(xi.subtract(xj)).mod(prime);
}
}
// L_i(0) = numerator / denominator
BigInteger lambda = numerator.multiply(denominator.modInverse(prime)).mod(prime);
// secret += yi * L_i(0)
secret = secret.add(yi.multiply(lambda)).mod(prime);
}
return secret;
}
/**
* Evaluate polynomial at given x
*/
private static BigInteger evaluatePolynomial(List<BigInteger> coefficients,
BigInteger x, BigInteger prime) {
// f(x) = a0 + a1*x + a2*x^2 + ... + at-1*x^(t-1)
BigInteger result = BigInteger.ZERO;
BigInteger power = BigInteger.ONE;
for (BigInteger coeff : coefficients) {
result = result.add(coeff.multiply(power)).mod(prime);
power = power.multiply(x).mod(prime);
}
return result;
}
/**
* Verifiable Secret Sharing (VSS) with commitments
*/
public static class VerifiableSharing {
/**
* Create verifiable shares with commitments
*/
public static VerifiableSecret splitWithCommitments(
BigInteger secret, int totalShares, int threshold,
ECPoint generator, BigInteger prime) {
List<BigInteger> coefficients = new ArrayList<>();
coefficients.add(secret);
// Generate random coefficients
for (int i = 1; i < threshold; i++) {
coefficients.add(generateRandomModPrime(prime));
}
// Generate commitments: C_i = g^coeff_i
List<ECPoint> commitments = new ArrayList<>();
for (BigInteger coeff : coefficients) {
commitments.add(generator.multiply(coeff));
}
// Generate shares
List<Share> shares = new ArrayList<>();
for (int i = 1; i <= totalShares; i++) {
BigInteger x = BigInteger.valueOf(i);
BigInteger y = evaluatePolynomial(coefficients, x, prime);
shares.add(new Share(i, y));
}
return new VerifiableSecret(shares, commitments);
}
/**
* Verify a share against commitments
*/
public static boolean verifyShare(Share share, List<ECPoint> commitments,
ECPoint generator, BigInteger prime) {
// Compute g^f(x)
ECPoint left = generator.multiply(share.y);
// Compute product of C_i^(x^i)
ECPoint right = commitments.get(0); // C0
BigInteger power = share.x;
for (int i = 1; i < commitments.size(); i++) {
ECPoint commitment = commitments.get(i);
right = right.add(commitment.multiply(BigInteger.valueOf(share.x).pow(i)));
power = power.multiply(BigInteger.valueOf(share.x));
}
return left.equals(right);
}
}
/**
* Share representation
*/
public static class Share {
public final int x; // x-coordinate (index)
public final BigInteger y; // y-coordinate (value)
public Share(int x, BigInteger y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return String.format("(%d, %s)", x, y.toString(16).substring(0, 20) + "...");
}
}
/**
* Verifiable secret with commitments
*/
public static class VerifiableSecret {
public final List<Share> shares;
public final List<ECPoint> commitments;
public VerifiableSecret(List<Share> shares, List<ECPoint> commitments) {
this.shares = shares;
this.commitments = commitments;
}
}
private static BigInteger generateRandomModPrime(BigInteger prime) {
BigInteger rand;
do {
rand = new BigInteger(prime.bitLength(), SECURE_RANDOM);
} while (rand.compareTo(prime) >= 0 || rand.equals(BigInteger.ZERO));
return rand;
}
}
3. Distributed Key Generation (DKG)
package com.threshold.dkg;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* Distributed Key Generation Protocol (based on Pedersen's DKG)
*/
public class DistributedKeyGeneration {
private final int threshold;
private final int totalParticipants;
private final int participantId;
private final ECPoint generator;
private final BigInteger order;
private Map<Integer, Participant> participants;
private BigInteger privateShare;
private ECPoint publicKey;
private boolean completed;
public DistributedKeyGeneration(int participantId, int threshold,
int totalParticipants, ECPoint generator,
BigInteger order) {
this.participantId = participantId;
this.threshold = threshold;
this.totalParticipants = totalParticipants;
this.generator = generator;
this.order = order;
this.participants = new ConcurrentHashMap<>();
this.completed = false;
}
/**
* Round 1: Generate and distribute commitments
*/
public Round1Message round1() {
// Generate random polynomial f_i(z) = a_i0 + a_i1*z + ... + a_i{t-1}*z^{t-1}
List<BigInteger> coefficients = new ArrayList<>();
for (int j = 0; j < threshold; j++) {
coefficients.add(generateRandomModOrder());
}
// Store coefficients for later use
this.privateCoefficients = coefficients;
// Generate commitments C_ij = g^a_ij
List<ECPoint> commitments = new ArrayList<>();
for (BigInteger coeff : coefficients) {
commitments.add(generator.multiply(coeff));
}
// Generate zero-knowledge proof for discrete log equality
Proof proof = generateProof(coefficients.get(0));
return new Round1Message(participantId, commitments, proof);
}
/**
* Round 2: Verify commitments and send encrypted shares
*/
public Round2Message round2(Map<Integer, Round1Message> round1Messages) {
// Verify all participants' commitments
for (Map.Entry<Integer, Round1Message> entry : round1Messages.entrySet()) {
int id = entry.getKey();
Round1Message msg = entry.getValue();
if (!verifyProof(msg.commitments.get(0), msg.proof)) {
throw new SecurityException("Invalid proof from participant " + id);
}
participants.put(id, new Participant(id, msg.commitments));
}
// Compute shares for other participants
Map<Integer, BigInteger> encryptedShares = new HashMap<>();
for (int recipientId = 1; recipientId <= totalParticipants; recipientId++) {
if (recipientId == participantId) continue;
// f_i(j) for j = recipientId
BigInteger share = evaluatePolynomial(privateCoefficients,
BigInteger.valueOf(recipientId));
// Encrypt share using recipient's public key (simplified)
BigInteger encryptedShare = encryptShare(share, recipientId);
encryptedShares.put(recipientId, encryptedShare);
}
// Compute own share
BigInteger ownShare = evaluatePolynomial(privateCoefficients,
BigInteger.valueOf(participantId));
// Store own share
this.partialPrivateShares.put(participantId, ownShare);
return new Round2Message(participantId, encryptedShares);
}
/**
* Final round: Verify shares and compute final private key share
*/
public void finalize(Map<Integer, Round2Message> round2Messages) {
// Verify received shares
for (Map.Entry<Integer, Round2Message> entry : round2Messages.entrySet()) {
int senderId = entry.getKey();
Round2Message msg = entry.getValue();
// Get our share from sender
BigInteger receivedShare = msg.encryptedShares.get(participantId);
BigInteger decryptedShare = decryptShare(receivedShare, senderId);
// Verify share against sender's commitments
Participant sender = participants.get(senderId);
if (!verifyShare(decryptedShare, sender.commitments, participantId)) {
throw new SecurityException("Invalid share from participant " + senderId);
}
partialPrivateShares.put(senderId, decryptedShare);
}
// Compute final private key share
this.privateShare = BigInteger.ZERO;
for (BigInteger share : partialPrivateShares.values()) {
this.privateShare = this.privateShare.add(share).mod(order);
}
// Compute public key
this.publicKey = computePublicKey();
this.completed = true;
}
/**
* Get the participant's private key share
*/
public BigInteger getPrivateShare() {
if (!completed) {
throw new IllegalStateException("DKG not completed");
}
return privateShare;
}
/**
* Get the group public key
*/
public ECPoint getPublicKey() {
if (!completed) {
throw new IllegalStateException("DKG not completed");
}
return publicKey;
}
private List<BigInteger> privateCoefficients;
private Map<Integer, BigInteger> partialPrivateShares = new HashMap<>();
/**
* Participant information
*/
private static class Participant {
final int id;
final List<ECPoint> commitments;
Participant(int id, List<ECPoint> commitments) {
this.id = id;
this.commitments = commitments;
}
}
/**
* Round 1 message
*/
public static class Round1Message {
public final int senderId;
public final List<ECPoint> commitments;
public final Proof proof;
public Round1Message(int senderId, List<ECPoint> commitments, Proof proof) {
this.senderId = senderId;
this.commitments = commitments;
this.proof = proof;
}
}
/**
* Round 2 message
*/
public static class Round2Message {
public final int senderId;
public final Map<Integer, BigInteger> encryptedShares;
public Round2Message(int senderId, Map<Integer, BigInteger> encryptedShares) {
this.senderId = senderId;
this.encryptedShares = encryptedShares;
}
}
/**
* Zero-knowledge proof for discrete log equality
*/
public static class Proof {
public final BigInteger challenge;
public final BigInteger response;
public Proof(BigInteger challenge, BigInteger response) {
this.challenge = challenge;
this.response = response;
}
}
private BigInteger generateRandomModOrder() {
return new BigInteger(order.bitLength(), new SecureRandom()).mod(order);
}
private BigInteger evaluatePolynomial(List<BigInteger> coefficients, BigInteger x) {
BigInteger result = BigInteger.ZERO;
BigInteger power = BigInteger.ONE;
for (BigInteger coeff : coefficients) {
result = result.add(coeff.multiply(power)).mod(order);
power = power.multiply(x).mod(order);
}
return result;
}
private Proof generateProof(BigInteger secret) {
// Schnorr-like proof of knowledge
BigInteger random = generateRandomModOrder();
ECPoint commitment = generator.multiply(random);
// Challenge = H(commitment)
BigInteger challenge = hash(commitment);
// Response = random - challenge * secret mod order
BigInteger response = random.subtract(challenge.multiply(secret)).mod(order);
return new Proof(challenge, response);
}
private boolean verifyProof(ECPoint publicKey, Proof proof) {
// Verify that g^response * publicKey^challenge == commitment
ECPoint left = generator.multiply(proof.response);
ECPoint right = publicKey.multiply(proof.challenge);
ECPoint computed = left.add(right);
// Recompute challenge
BigInteger expectedChallenge = hash(computed);
return expectedChallenge.equals(proof.challenge);
}
private boolean verifyShare(BigInteger share, List<ECPoint> commitments, int index) {
// Verify g^share == product of C_i^(index^i)
ECPoint left = generator.multiply(share);
ECPoint right = commitments.get(0);
BigInteger power = BigInteger.valueOf(index);
for (int i = 1; i < commitments.size(); i++) {
right = right.add(commitments.get(i).multiply(power));
power = power.multiply(BigInteger.valueOf(index));
}
return left.equals(right);
}
private BigInteger hash(ECPoint point) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(point.getEncoded(false));
return new BigInteger(1, hash).mod(order);
} catch (Exception e) {
throw new RuntimeException("Hashing failed", e);
}
}
private ECPoint computePublicKey() {
// Public key = g^(sum of a_i0)
BigInteger sum = BigInteger.ZERO;
for (Participant p : participants.values()) {
// a_i0 is the constant term (first commitment)
ECPoint commitment = p.commitments.get(0);
// This is simplified - real implementation would aggregate
}
return generator.multiply(sum);
}
private BigInteger encryptShare(BigInteger share, int recipientId) {
// Simplified encryption - in practice use proper encryption
return share.add(BigInteger.valueOf(recipientId * 1000000)).mod(order);
}
private BigInteger decryptShare(BigInteger encrypted, int senderId) {
// Simplified decryption
return encrypted.subtract(BigInteger.valueOf(senderId * 1000000)).mod(order);
}
}
4. Threshold ECDSA Implementation (GG18)
package com.threshold.ecdsa;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import java.math.BigInteger;
import java.security.*;
import java.util.*;
/**
* Threshold ECDSA implementation based on GG18 protocol
*/
public class ThresholdECDSA {
private static final String CURVE_NAME = "secp256k1";
private static final ECParameterSpec CURVE_PARAMS =
ECNamedCurveTable.getParameterSpec(CURVE_NAME);
private final int threshold;
private final int totalParticipants;
private final Map<Integer, Participant> participants;
private final SecureRandom secureRandom;
public ThresholdECDSA(int threshold, int totalParticipants) {
this.threshold = threshold;
this.totalParticipants = totalParticipants;
this.participants = new HashMap<>();
this.secureRandom = new SecureRandom();
}
/**
* Participant in threshold signing
*/
public static class Participant {
public final int id;
public BigInteger privateShare;
public ECPoint publicShare;
public List<ECPoint> commitments;
public Participant(int id) {
this.id = id;
}
}
/**
* Signing session
*/
public class SigningSession {
private final byte[] message;
private final BigInteger messageHash;
private final Set<Integer> participatingParties;
private final Map<Integer, Round1Message> round1Messages;
private final Map<Integer, Round2Message> round2Messages;
private BigInteger k; // ephemeral private key
private BigInteger gamma; // inverse of k
public SigningSession(byte[] message, Set<Integer> parties) {
this.message = message;
this.messageHash = hashMessage(message);
this.participatingParties = parties;
this.round1Messages = new HashMap<>();
this.round2Messages = new HashMap<>();
}
/**
* Round 1: Generate and distribute ephemeral keys
*/
public Round1Message round1(Participant participant) {
// Generate ephemeral key pair
BigInteger ki = generateRandomModOrder();
ECPoint Ri = CURVE_PARAMS.getG().multiply(ki);
// Generate zero-knowledge proof for ephemeral key
Proof proof = generateProof(ki);
Round1Message msg = new Round1Message(participant.id, Ri, proof);
round1Messages.put(participant.id, msg);
return msg;
}
/**
* Round 2: Compute partial signatures
*/
public Round2Message round2(Participant participant,
Map<Integer, Round1Message> received) {
// Verify all received proofs
for (Map.Entry<Integer, Round1Message> entry : received.entrySet()) {
if (!verifyProof(entry.getValue().Ri, entry.getValue().proof)) {
throw new SecurityException("Invalid proof from party " + entry.getKey());
}
}
// Compute R = sum(Ri)
ECPoint R = CURVE_PARAMS.getG().multiply(BigInteger.ZERO);
for (Round1Message msg : received.values()) {
R = R.add(msg.Ri);
}
// Compute r = x-coordinate of R mod order
BigInteger r = R.getXCoord().toBigInteger().mod(CURVE_PARAMS.getN());
// Compute partial signature
BigInteger share = participant.privateShare;
BigInteger ki = getEphemeralKey(participant.id);
// si = ki * (messageHash + r * share) mod order
BigInteger s1 = r.multiply(share).mod(CURVE_PARAMS.getN());
BigInteger s2 = messageHash.add(s1).mod(CURVE_PARAMS.getN());
BigInteger si = ki.multiply(s2).mod(CURVE_PARAMS.getN());
Round2Message msg = new Round2Message(participant.id, si);
round2Messages.put(participant.id, msg);
return msg;
}
/**
* Finalize: Combine partial signatures
*/
public Signature finalize(Map<Integer, Round2Message> received) {
// Sum all partial signatures
BigInteger s = BigInteger.ZERO;
for (Round2Message msg : received.values()) {
s = s.add(msg.si).mod(CURVE_PARAMS.getN());
}
// Compute r from R
ECPoint R = computeAggregateR();
BigInteger r = R.getXCoord().toBigInteger().mod(CURVE_PARAMS.getN());
return new Signature(r, s);
}
private ECPoint computeAggregateR() {
ECPoint R = CURVE_PARAMS.getG().multiply(BigInteger.ZERO);
for (Round1Message msg : round1Messages.values()) {
R = R.add(msg.Ri);
}
return R;
}
private BigInteger getEphemeralKey(int partyId) {
// In practice, this would be derived from the DKG for ephemeral keys
return BigInteger.valueOf(partyId).multiply(BigInteger.valueOf(1000000))
.mod(CURVE_PARAMS.getN());
}
}
/**
* Round 1 message
*/
public static class Round1Message {
public final int partyId;
public final ECPoint Ri;
public final Proof proof;
public Round1Message(int partyId, ECPoint ri, Proof proof) {
this.partyId = partyId;
this.Ri = ri;
this.proof = proof;
}
}
/**
* Round 2 message
*/
public static class Round2Message {
public final int partyId;
public final BigInteger si;
public Round2Message(int partyId, BigInteger si) {
this.partyId = partyId;
this.si = si;
}
}
/**
* ECDSA Signature
*/
public static class Signature {
public final BigInteger r;
public final BigInteger s;
public Signature(BigInteger r, BigInteger s) {
this.r = r;
this.s = s;
}
public byte[] encode() {
byte[] rBytes = r.toByteArray();
byte[] sBytes = s.toByteArray();
byte[] result = new byte[rBytes.length + sBytes.length];
System.arraycopy(rBytes, 0, result, 0, rBytes.length);
System.arraycopy(sBytes, 0, result, rBytes.length, sBytes.length);
return result;
}
}
/**
* Zero-knowledge proof for discrete log
*/
public static class Proof {
public final BigInteger challenge;
public final BigInteger response;
public Proof(BigInteger challenge, BigInteger response) {
this.challenge = challenge;
this.response = response;
}
}
private BigInteger generateRandomModOrder() {
return new BigInteger(CURVE_PARAMS.getN().bitLength(), secureRandom)
.mod(CURVE_PARAMS.getN());
}
private Proof generateProof(BigInteger secret) {
BigInteger random = generateRandomModOrder();
ECPoint commitment = CURVE_PARAMS.getG().multiply(random);
// Challenge = H(commitment)
BigInteger challenge = hash(commitment);
// Response = random - challenge * secret mod order
BigInteger response = random.subtract(challenge.multiply(secret))
.mod(CURVE_PARAMS.getN());
return new Proof(challenge, response);
}
private boolean verifyProof(ECPoint publicKey, Proof proof) {
// Verify that g^response * publicKey^challenge == commitment
ECPoint left = CURVE_PARAMS.getG().multiply(proof.response);
ECPoint right = publicKey.multiply(proof.challenge);
ECPoint computed = left.add(right);
// Recompute challenge
BigInteger expectedChallenge = hash(computed);
return expectedChallenge.equals(proof.challenge);
}
private BigInteger hash(ECPoint point) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(point.getEncoded(false));
return new BigInteger(1, hash).mod(CURVE_PARAMS.getN());
} catch (Exception e) {
throw new RuntimeException("Hashing failed", e);
}
}
private BigInteger hashMessage(byte[] message) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(message);
return new BigInteger(1, hash).mod(CURVE_PARAMS.getN());
} catch (Exception e) {
throw new RuntimeException("Hashing failed", e);
}
}
/**
* Verify threshold signature
*/
public boolean verify(byte[] message, Signature signature, ECPoint publicKey) {
BigInteger r = signature.r;
BigInteger s = signature.s;
BigInteger z = hashMessage(message);
BigInteger n = CURVE_PARAMS.getN();
// s must be in [1, n-1]
if (s.compareTo(BigInteger.ONE) < 0 || s.compareTo(n) >= 0) {
return false;
}
// Calculate w = s^-1 mod n
BigInteger w = s.modInverse(n);
// u1 = z * w mod n, u2 = r * w mod n
BigInteger u1 = z.multiply(w).mod(n);
BigInteger u2 = r.multiply(w).mod(n);
// (x1, y1) = u1*G + u2*publicKey
ECPoint point = CURVE_PARAMS.getG().multiply(u1)
.add(publicKey.multiply(u2));
// v = x1 mod n
BigInteger v = point.getXCoord().toBigInteger().mod(n);
return v.equals(r);
}
}
5. FROST (Flexible Round-Optimized Schnorr Threshold) Signatures
package com.threshold.frost;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.*;
/**
* FROST: Flexible Round-Optimized Schnorr Threshold Signatures
*/
public class FROST {
private final int threshold;
private final int totalParticipants;
private final ECPoint generator;
private final BigInteger order;
private final SecureRandom secureRandom;
public FROST(int threshold, int totalParticipants,
ECPoint generator, BigInteger order) {
this.threshold = threshold;
this.totalParticipants = totalParticipants;
this.generator = generator;
this.order = order;
this.secureRandom = new SecureRandom();
}
/**
* Trusted dealer key generation (for testing/setup)
*/
public KeyPackage trustedDealerKeyGen(BigInteger secret) {
// Create polynomial f(x) of degree threshold-1
List<BigInteger> coefficients = new ArrayList<>();
coefficients.add(secret);
for (int i = 1; i < threshold; i++) {
coefficients.add(generateRandomModOrder());
}
// Generate shares for all participants
Map<Integer, BigInteger> shares = new HashMap<>();
Map<Integer, List<ECPoint>> commitments = new HashMap<>();
for (int i = 1; i <= totalParticipants; i++) {
BigInteger share = evaluatePolynomial(coefficients, BigInteger.valueOf(i));
shares.put(i, share);
// Generate commitments for verification
List<ECPoint> participantCommitments = new ArrayList<>();
for (BigInteger coeff : coefficients) {
participantCommitments.add(generator.multiply(coeff));
}
commitments.put(i, participantCommitments);
}
// Public key = g^secret
ECPoint publicKey = generator.multiply(secret);
return new KeyPackage(shares, commitments, publicKey);
}
/**
* Distributed key generation for FROST
*/
public class DistributedKeyGen {
private final int participantId;
private final Map<Integer, List<ECPoint>> commitments;
private final Map<Integer, BigInteger> privateShares;
private BigInteger privateShare;
private ECPoint publicKey;
private boolean completed;
public DistributedKeyGen(int participantId) {
this.participantId = participantId;
this.commitments = new HashMap<>();
this.privateShares = new HashMap<>();
this.completed = false;
}
/**
* Round 1: Generate polynomial and commitments
*/
public DkgRound1Message round1() {
// Generate random polynomial of degree threshold-1
List<BigInteger> coefficients = new ArrayList<>();
coefficients.add(generateRandomModOrder()); // a0 is participant's secret
for (int i = 1; i < threshold; i++) {
coefficients.add(generateRandomModOrder());
}
// Store coefficients for later
storeCoefficients(coefficients);
// Generate commitments
List<ECPoint> participantCommitments = new ArrayList<>();
for (BigInteger coeff : coefficients) {
participantCommitments.add(generator.multiply(coeff));
}
return new DkgRound1Message(participantId, participantCommitments);
}
/**
* Round 2: Verify commitments and send encrypted shares
*/
public DkgRound2Message round2(Map<Integer, DkgRound1Message> messages) {
// Verify all commitments
for (DkgRound1Message msg : messages.values()) {
commitments.put(msg.senderId, msg.commitments);
}
// Compute shares for other participants
Map<Integer, BigInteger> shares = new HashMap<>();
for (int i = 1; i <= totalParticipants; i++) {
if (i == participantId) continue;
BigInteger share = getShareForParticipant(i);
// In practice, encrypt share for recipient
shares.put(i, share);
}
// Compute own share
BigInteger ownShare = getShareForParticipant(participantId);
privateShares.put(participantId, ownShare);
return new DkgRound2Message(participantId, shares);
}
/**
* Finalize: Aggregate shares and compute public key
*/
public void finalize(Map<Integer, DkgRound2Message> messages) {
// Verify and collect shares from others
for (Map.Entry<Integer, DkgRound2Message> entry : messages.entrySet()) {
int senderId = entry.getKey();
DkgRound2Message msg = entry.getValue();
BigInteger share = msg.encryptedShares.get(participantId);
// In practice, decrypt share
// Verify share against sender's commitments
List<ECPoint> senderCommitments = commitments.get(senderId);
if (!verifyShare(share, senderCommitments, participantId)) {
throw new SecurityException("Invalid share from " + senderId);
}
privateShares.put(senderId, share);
}
// Compute final private key share
privateShare = BigInteger.ZERO;
for (BigInteger share : privateShares.values()) {
privateShare = privateShare.add(share).mod(order);
}
// Compute public key (sum of all participants' first commitments)
publicKey = generator.multiply(BigInteger.ZERO);
for (List<ECPoint> comms : commitments.values()) {
publicKey = publicKey.add(comms.get(0));
}
completed = true;
}
public BigInteger getPrivateShare() {
if (!completed) throw new IllegalStateException("DKG not completed");
return privateShare;
}
public ECPoint getPublicKey() {
if (!completed) throw new IllegalStateException("DKG not completed");
return publicKey;
}
}
/**
* FROST Signing Session
*/
public class SigningSession {
private final byte[] message;
private final BigInteger messageHash;
private final Set<Integer> signers;
private final int threshold;
private final Map<Integer, BigInteger> bindingFactors;
private final Map<Integer, ECPoint> commitmentShares;
private final Map<Integer, BigInteger> responseShares;
public SigningSession(byte[] message, Set<Integer> signers, int threshold) {
this.message = message;
this.messageHash = hashMessage(message);
this.signers = signers;
this.threshold = threshold;
this.bindingFactors = new HashMap<>();
this.commitmentShares = new HashMap<>();
this.responseShares = new HashMap<>();
if (signers.size() < threshold) {
throw new IllegalArgumentException("Not enough signers");
}
}
/**
* Round 1: Generate nonces and commitments
*/
public Round1Message round1(int participantId, BigInteger privateShare) {
// Generate two random nonces (d, e) for each signing session
BigInteger d = generateRandomModOrder();
BigInteger e = generateRandomModOrder();
// Store nonces for later use
storeNonces(participantId, d, e);
// Compute commitments: D = g^d, E = g^e
ECPoint D = generator.multiply(d);
ECPoint E = generator.multiply(e);
return new Round1Message(participantId, D, E);
}
/**
* Round 2: Compute binding factors and response shares
*/
public Round2Message round2(int participantId,
Map<Integer, Round1Message> commitments,
BigInteger privateShare) {
// Compute binding factors for all signers
List<ECPoint> commitmentList = new ArrayList<>();
List<Integer> signerIds = new ArrayList<>(commitments.keySet());
for (Integer id : signerIds) {
Round1Message msg = commitments.get(id);
commitmentList.add(msg.D);
commitmentList.add(msg.E);
}
// ρ_i = H(i, message, commitmentList)
BigInteger bindingFactor = computeBindingFactor(
participantId, message, commitmentList);
bindingFactors.put(participantId, bindingFactor);
// Compute group commitment R = product of (D_i * E_i^ρ_i)
ECPoint R = computeGroupCommitment(commitments, bindingFactors);
BigInteger r = R.getXCoord().toBigInteger().mod(order);
// Compute response share z_i = d_i + e_i*ρ_i + λ_i * s_i * c
BigInteger lambda = computeLagrangeCoefficient(
participantId, signerIds, order);
BigInteger d = getNonceD(participantId);
BigInteger e = getNonceE(participantId);
BigInteger c = r; // challenge
// z_i = d + e*ρ + λ * s * c
BigInteger term1 = d;
BigInteger term2 = e.multiply(bindingFactor).mod(order);
BigInteger term3 = lambda.multiply(privateShare).multiply(c).mod(order);
BigInteger z = term1.add(term2).add(term3).mod(order);
return new Round2Message(participantId, z);
}
/**
* Finalize: Aggregate responses and verify signature
*/
public Signature finalize(Map<Integer, Round2Message> responses,
Map<Integer, Round1Message> commitments,
ECPoint publicKey) {
// Aggregate responses: z = sum(z_i)
BigInteger z = BigInteger.ZERO;
for (Round2Message msg : responses.values()) {
z = z.add(msg.z).mod(order);
}
// Recompute group commitment R
ECPoint R = computeGroupCommitment(commitments, bindingFactors);
BigInteger r = R.getXCoord().toBigInteger().mod(order);
// Verify signature
Signature sig = new Signature(r, z);
if (!verify(message, sig, publicKey)) {
throw new SecurityException("Signature verification failed");
}
return sig;
}
private ECPoint computeGroupCommitment(
Map<Integer, Round1Message> commitments,
Map<Integer, BigInteger> bindingFactors) {
ECPoint R = generator.multiply(BigInteger.ZERO);
for (Map.Entry<Integer, Round1Message> entry : commitments.entrySet()) {
int id = entry.getKey();
Round1Message msg = entry.getValue();
BigInteger rho = bindingFactors.get(id);
// D_i * E_i^ρ_i
ECPoint term = msg.D.add(msg.E.multiply(rho));
R = R.add(term);
}
return R;
}
}
// Helper classes
public static class KeyPackage {
public final Map<Integer, BigInteger> shares;
public final Map<Integer, List<ECPoint>> commitments;
public final ECPoint publicKey;
public KeyPackage(Map<Integer, BigInteger> shares,
Map<Integer, List<ECPoint>> commitments,
ECPoint publicKey) {
this.shares = shares;
this.commitments = commitments;
this.publicKey = publicKey;
}
}
public static class DkgRound1Message {
public final int senderId;
public final List<ECPoint> commitments;
public DkgRound1Message(int senderId, List<ECPoint> commitments) {
this.senderId = senderId;
this.commitments = commitments;
}
}
public static class DkgRound2Message {
public final int senderId;
public final Map<Integer, BigInteger> encryptedShares;
public DkgRound2Message(int senderId, Map<Integer, BigInteger> encryptedShares) {
this.senderId = senderId;
this.encryptedShares = encryptedShares;
}
}
public static class Round1Message {
public final int participantId;
public final ECPoint D;
public final ECPoint E;
public Round1Message(int participantId, ECPoint D, ECPoint E) {
this.participantId = participantId;
this.D = D;
this.E = E;
}
}
public static class Round2Message {
public final int participantId;
public final BigInteger z;
public Round2Message(int participantId, BigInteger z) {
this.participantId = participantId;
this.z = z;
}
}
public static class Signature {
public final BigInteger r;
public final BigInteger z;
public Signature(BigInteger r, BigInteger z) {
this.r = r;
this.z = z;
}
}
// Helper methods
private BigInteger generateRandomModOrder() {
return new BigInteger(order.bitLength(), secureRandom).mod(order);
}
private BigInteger evaluatePolynomial(List<BigInteger> coefficients, BigInteger x) {
BigInteger result = BigInteger.ZERO;
BigInteger power = BigInteger.ONE;
for (BigInteger coeff : coefficients) {
result = result.add(coeff.multiply(power)).mod(order);
power = power.multiply(x).mod(order);
}
return result;
}
private boolean verifyShare(BigInteger share, List<ECPoint> commitments, int index) {
ECPoint left = generator.multiply(share);
ECPoint right = commitments.get(0);
BigInteger power = BigInteger.valueOf(index);
for (int i = 1; i < commitments.size(); i++) {
right = right.add(commitments.get(i).multiply(power));
power = power.multiply(BigInteger.valueOf(index));
}
return left.equals(right);
}
private BigInteger computeBindingFactor(int participantId, byte[] message,
List<ECPoint> commitmentList) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(participantId);
digest.update(message);
for (ECPoint point : commitmentList) {
digest.update(point.getEncoded(false));
}
byte[] hash = digest.digest();
return new BigInteger(1, hash).mod(order);
} catch (Exception e) {
throw new RuntimeException("Failed to compute binding factor", e);
}
}
private BigInteger computeLagrangeCoefficient(int i, List<Integer> signers,
BigInteger order) {
// λ_i = product_{j in signers, j != i} (0 - j) / (i - j)
BigInteger lambda = BigInteger.ONE;
BigInteger zero = BigInteger.ZERO;
for (int j : signers) {
if (j == i) continue;
BigInteger numerator = zero.subtract(BigInteger.valueOf(j));
BigInteger denominator = BigInteger.valueOf(i).subtract(BigInteger.valueOf(j));
lambda = lambda.multiply(numerator)
.multiply(denominator.modInverse(order))
.mod(order);
}
return lambda;
}
private BigInteger hashMessage(byte[] message) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(message);
return new BigInteger(1, hash).mod(order);
} catch (Exception e) {
throw new RuntimeException("Hashing failed", e);
}
}
public boolean verify(byte[] message, Signature sig, ECPoint publicKey) {
BigInteger r = sig.r;
BigInteger z = sig.z;
BigInteger c = hashMessage(message);
// Compute R' = g^z * publicKey^(-c)
ECPoint Rprime = generator.multiply(z)
.add(publicKey.multiply(c.negate().mod(order)));
BigInteger rprime = Rprime.getXCoord().toBigInteger().mod(order);
return rprime.equals(r);
}
// Storage for per-session nonces (in practice, use secure storage)
private final Map<Integer, BigInteger> nonceD = new HashMap<>();
private final Map<Integer, BigInteger> nonceE = new HashMap<>();
private final Map<Integer, List<BigInteger>> coefficients = new HashMap<>();
private void storeNonces(int participantId, BigInteger d, BigInteger e) {
nonceD.put(participantId, d);
nonceE.put(participantId, e);
}
private BigInteger getNonceD(int participantId) {
return nonceD.get(participantId);
}
private BigInteger getNonceE(int participantId) {
return nonceE.get(participantId);
}
private void storeCoefficients(List<BigInteger> coeffs) {
coefficients.put(coefficients.size(), coeffs);
}
private BigInteger getShareForParticipant(int participantId) {
// Simplified - in practice, would use stored polynomial
return BigInteger.valueOf(participantId).multiply(BigInteger.valueOf(1000000))
.mod(order);
}
}
6. BLS Threshold Signatures
package com.threshold.bls;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.*;
/**
* BLS Threshold Signatures using pairing-based cryptography
*/
public class BLSThreshold {
private final int threshold;
private final int totalParticipants;
private final PairingParameters pairing;
private final ECPoint generator;
private final BigInteger order;
private final SecureRandom secureRandom;
public BLSThreshold(int threshold, int totalParticipants,
PairingParameters pairing,
ECPoint generator,
BigInteger order) {
this.threshold = threshold;
this.totalParticipants = totalParticipants;
this.pairing = pairing;
this.generator = generator;
this.order = order;
this.secureRandom = new SecureRandom();
}
/**
* Key generation for BLS threshold signatures
*/
public KeyPackage keygen() {
// Generate master secret key
BigInteger masterSecret = generateRandomModOrder();
// Generate polynomial of degree threshold-1
List<BigInteger> coefficients = new ArrayList<>();
coefficients.add(masterSecret);
for (int i = 1; i < threshold; i++) {
coefficients.add(generateRandomModOrder());
}
// Generate shares for all participants
Map<Integer, BigInteger> secretShares = new HashMap<>();
Map<Integer, ECPoint> publicShares = new HashMap<>();
for (int i = 1; i <= totalParticipants; i++) {
BigInteger x = BigInteger.valueOf(i);
BigInteger share = evaluatePolynomial(coefficients, x);
secretShares.put(i, share);
// Public share = g^share
publicShares.put(i, generator.multiply(share));
}
// Master public key = g^masterSecret
ECPoint masterPublicKey = generator.multiply(masterSecret);
return new KeyPackage(secretShares, publicShares, masterPublicKey);
}
/**
* Sign a message with a secret key share
*/
public byte[] sign(byte[] message, BigInteger secretShare) {
// H(m) - hash to curve
ECPoint hash = hashToCurve(message);
// σ_i = H(m)^sk_i
ECPoint signature = hash.multiply(secretShare);
return signature.getEncoded(true);
}
/**
* Verify a signature share
*/
public boolean verifyShare(byte[] message, byte[] signatureShare,
ECPoint publicShare) {
ECPoint sig = pairing.getCurve().decodePoint(signatureShare);
ECPoint hash = hashToCurve(message);
// e(σ_i, g) == e(H(m), pk_i)
PairingResult left = pairing.pair(sig, generator);
PairingResult right = pairing.pair(hash, publicShare);
return left.equals(right);
}
/**
* Aggregate signature shares to produce final signature
*/
public byte[] aggregate(byte[] message, List<byte[]> signatureShares,
List<ECPoint> publicShares,
List<Integer> participantIndices) {
if (signatureShares.size() < threshold) {
throw new IllegalArgumentException("Not enough signature shares");
}
// Compute Lagrange coefficients
Map<Integer, BigInteger> lambdas = computeLagrangeCoefficients(
participantIndices, order);
// Aggregate signatures: σ = ∏ σ_i^λ_i
ECPoint aggregated = generator.multiply(BigInteger.ZERO);
for (int i = 0; i < signatureShares.size(); i++) {
int index = participantIndices.get(i);
BigInteger lambda = lambdas.get(index);
ECPoint share = pairing.getCurve().decodePoint(signatureShares.get(i));
aggregated = aggregated.add(share.multiply(lambda));
}
return aggregated.getEncoded(true);
}
/**
* Verify aggregated signature
*/
public boolean verify(byte[] message, byte[] aggregatedSignature,
ECPoint masterPublicKey) {
ECPoint sig = pairing.getCurve().decodePoint(aggregatedSignature);
ECPoint hash = hashToCurve(message);
// e(σ, g) == e(H(m), pk)
PairingResult left = pairing.pair(sig, generator);
PairingResult right = pairing.pair(hash, masterPublicKey);
return left.equals(right);
}
/**
* Batch verify multiple signatures
*/
public boolean batchVerify(List<byte[]> messages,
List<byte[]> signatures,
ECPoint masterPublicKey) {
if (messages.size() != signatures.size()) {
throw new IllegalArgumentException("Message and signature count mismatch");
}
// Random coefficients for batching
List<BigInteger> coeffs = new ArrayList<>();
for (int i = 0; i < messages.size(); i++) {
coeffs.add(generateRandomModOrder());
}
// Compute product of (e(σ_i, g)^c_i)
PairingResult left = PairingResult.one();
PairingResult right = PairingResult.one();
for (int i = 0; i < messages.size(); i++) {
ECPoint sig = pairing.getCurve().decodePoint(signatures.get(i));
ECPoint hash = hashToCurve(messages.get(i));
left = left.multiply(pairing.pair(sig, generator).pow(coeffs.get(i)));
right = right.multiply(pairing.pair(hash, masterPublicKey).pow(coeffs.get(i)));
}
return left.equals(right);
}
/**
* Proactive security: refresh shares without changing public key
*/
public ShareRefresh refreshShares(Map<Integer, BigInteger> currentShares) {
// Generate random polynomial with zero constant term
List<BigInteger> deltaCoeffs = new ArrayList<>();
deltaCoeffs.add(BigInteger.ZERO); // a0 = 0
for (int i = 1; i < threshold; i++) {
deltaCoeffs.add(generateRandomModOrder());
}
// Compute delta shares
Map<Integer, BigInteger> deltaShares = new HashMap<>();
for (int i = 1; i <= totalParticipants; i++) {
BigInteger x = BigInteger.valueOf(i);
BigInteger delta = evaluatePolynomial(deltaCoeffs, x);
deltaShares.put(i, delta);
}
return new ShareRefresh(deltaShares, Collections.emptyList());
}
/**
* Apply refresh to get new shares
*/
public Map<Integer, BigInteger> applyRefresh(Map<Integer, BigInteger> oldShares,
ShareRefresh refresh) {
Map<Integer, BigInteger> newShares = new HashMap<>();
for (Map.Entry<Integer, BigInteger> entry : oldShares.entrySet()) {
int index = entry.getKey();
BigInteger oldShare = entry.getValue();
BigInteger delta = refresh.deltaShares.get(index);
newShares.put(index, oldShare.add(delta).mod(order));
}
return newShares;
}
// Helper classes
public static class KeyPackage {
public final Map<Integer, BigInteger> secretShares;
public final Map<Integer, ECPoint> publicShares;
public final ECPoint masterPublicKey;
public KeyPackage(Map<Integer, BigInteger> secretShares,
Map<Integer, ECPoint> publicShares,
ECPoint masterPublicKey) {
this.secretShares = secretShares;
this.publicShares = publicShares;
this.masterPublicKey = masterPublicKey;
}
}
public static class ShareRefresh {
public final Map<Integer, BigInteger> deltaShares;
public final List<byte[]> proofs;
public ShareRefresh(Map<Integer, BigInteger> deltaShares, List<byte[]> proofs) {
this.deltaShares = deltaShares;
this.proofs = proofs;
}
}
// Simplified pairing parameters
public static class PairingParameters {
private final ECCurve curve;
public PairingParameters(ECCurve curve) {
this.curve = curve;
}
public ECCurve getCurve() {
return curve;
}
public PairingResult pair(ECPoint p, ECPoint q) {
// In practice, implement actual pairing computation
return new PairingResult();
}
}
public static class PairingResult {
private final byte[] value;
public PairingResult() {
this.value = new byte[32];
new SecureRandom().nextBytes(value);
}
public boolean equals(PairingResult other) {
return Arrays.equals(this.value, other.value);
}
public PairingResult multiply(PairingResult other) {
// In practice, implement group operation
return this;
}
public PairingResult pow(BigInteger exponent) {
// In practice, implement exponentiation
return this;
}
public static PairingResult one() {
return new PairingResult();
}
}
// Helper methods
private BigInteger generateRandomModOrder() {
return new BigInteger(order.bitLength(), secureRandom).mod(order);
}
private BigInteger evaluatePolynomial(List<BigInteger> coefficients, BigInteger x) {
BigInteger result = BigInteger.ZERO;
BigInteger power = BigInteger.ONE;
for (BigInteger coeff : coefficients) {
result = result.add(coeff.multiply(power)).mod(order);
power = power.multiply(x).mod(order);
}
return result;
}
private ECPoint hashToCurve(byte[] message) {
try {
// Hash message to a point on the curve
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(message);
BigInteger x = new BigInteger(1, hash).mod(order);
// Find point with x-coordinate = hash
// In practice, use proper hash-to-curve algorithm
return generator.multiply(x);
} catch (Exception e) {
throw new RuntimeException("Hash to curve failed", e);
}
}
private Map<Integer, BigInteger> computeLagrangeCoefficients(
List<Integer> participants, BigInteger order) {
Map<Integer, BigInteger> lambdas = new HashMap<>();
for (int i : participants) {
BigInteger lambda = BigInteger.ONE;
BigInteger xi = BigInteger.valueOf(i);
for (int j : participants) {
if (i != j) {
BigInteger xj = BigInteger.valueOf(j);
// λ_i = product (0 - xj) / (xi - xj)
BigInteger numerator = xj.negate();
BigInteger denominator = xi.subtract(xj);
lambda = lambda.multiply(numerator)
.multiply(denominator.modInverse(order))
.mod(order);
}
}
lambdas.put(i, lambda);
}
return lambdas;
}
}
7. Secure Multi-Party Computation Network
package com.threshold.network;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.SecretKey;
import javax.net.ssl.SSLException;
import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.security.cert.CertificateException;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Secure network layer for MPC communication
*/
public class MPCNetwork implements Closeable {
private static final Logger logger = LoggerFactory.getLogger(MPCNetwork.class);
private final int localPartyId;
private final Map<Integer, PartyInfo> remoteParties;
private final EventLoopGroup bossGroup;
private final EventLoopGroup workerGroup;
private final Channel serverChannel;
private final Map<Integer, Channel> clientChannels;
private final Map<Integer, BlockingQueue<Object>> messageQueues;
private final AtomicInteger messageCounter;
private final SslContext sslContext;
public MPCNetwork(int localPartyId, Map<Integer, PartyInfo> remoteParties)
throws CertificateException, SSLException, InterruptedException {
this.localPartyId = localPartyId;
this.remoteParties = remoteParties;
this.bossGroup = new NioEventLoopGroup(1);
this.workerGroup = new NioEventLoopGroup();
this.clientChannels = new ConcurrentHashMap<>();
this.messageQueues = new ConcurrentHashMap<>();
this.messageCounter = new AtomicInteger(0);
// Initialize SSL
SelfSignedCertificate ssc = new SelfSignedCertificate();
this.sslContext = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
.build();
// Start server
this.serverChannel = startServer();
// Connect to all remote parties
connectToAllParties();
logger.info("MPC Network initialized for party {} with {} remote parties",
localPartyId, remoteParties.size());
}
/**
* Send a message to a specific party
*/
public void send(int partyId, Object message) {
Channel channel = clientChannels.get(partyId);
if (channel == null || !channel.isActive()) {
throw new RuntimeException("No active connection to party " + partyId);
}
MPCMessage msg = new MPCMessage(localPartyId, partyId,
messageCounter.incrementAndGet(), message);
channel.writeAndFlush(msg);
logger.debug("Sent message {} to party {}", msg.id, partyId);
}
/**
* Broadcast message to all parties
*/
public void broadcast(Object message) {
int msgId = messageCounter.incrementAndGet();
for (int partyId : remoteParties.keySet()) {
Channel channel = clientChannels.get(partyId);
if (channel != null && channel.isActive()) {
MPCMessage msg = new MPCMessage(localPartyId, partyId, msgId, message);
channel.writeAndFlush(msg);
}
}
logger.debug("Broadcast message {} to {} parties", msgId, remoteParties.size());
}
/**
* Receive next message from a specific party
*/
public Object receive(int partyId, long timeout, TimeUnit unit)
throws InterruptedException, TimeoutException {
BlockingQueue<Object> queue = messageQueues.computeIfAbsent(
partyId, k -> new LinkedBlockingQueue<>());
Object message = queue.poll(timeout, unit);
if (message == null) {
throw new TimeoutException("Timeout waiting for message from party " + partyId);
}
return message;
}
/**
* Receive message with expected type
*/
public <T> T receive(int partyId, Class<T> expectedType, long timeout, TimeUnit unit)
throws InterruptedException, TimeoutException {
Object message = receive(partyId, timeout, unit);
if (!expectedType.isInstance(message)) {
throw new RuntimeException("Received unexpected message type: "
+ message.getClass().getName());
}
return expectedType.cast(message);
}
/**
* Receive all messages from all parties for a given round
*/
public <T> Map<Integer, T> receiveAll(Class<T> expectedType,
long timeout, TimeUnit unit)
throws InterruptedException, TimeoutException {
Map<Integer, T> messages = new ConcurrentHashMap<>();
CountDownLatch latch = new CountDownLatch(remoteParties.size());
for (int partyId : remoteParties.keySet()) {
new Thread(() -> {
try {
T msg = receive(partyId, expectedType, timeout, unit);
messages.put(partyId, msg);
latch.countDown();
} catch (Exception e) {
logger.error("Failed to receive from party {}", partyId, e);
}
}).start();
}
if (!latch.await(timeout, unit)) {
throw new TimeoutException("Not all parties responded in time");
}
return messages;
}
/**
* Synchronized broadcast with confirmation
*/
public <T> Map<Integer, T> broadcastSync(Object message, Class<T> responseType,
long timeout, TimeUnit unit)
throws InterruptedException, TimeoutException {
broadcast(message);
return receiveAll(responseType, timeout, unit);
}
private Channel startServer() throws InterruptedException {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(
new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
new ObjectEncoder(),
new MPCServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
PartyInfo localInfo = remoteParties.get(localPartyId);
return bootstrap.bind(localInfo.port).sync().channel();
}
private void connectToAllParties() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(remoteParties.size() - 1); // exclude self
for (Map.Entry<Integer, PartyInfo> entry : remoteParties.entrySet()) {
int partyId = entry.getKey();
if (partyId == localPartyId) continue;
PartyInfo info = entry.getValue();
new Thread(() -> {
try {
connectToParty(partyId, info);
latch.countDown();
} catch (Exception e) {
logger.error("Failed to connect to party {}:{}",
partyId, info.port, e);
}
}).start();
}
latch.await(30, TimeUnit.SECONDS);
logger.info("Connected to all {} remote parties", remoteParties.size() - 1);
}
private void connectToParty(int partyId, PartyInfo info) throws InterruptedException {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(workerGroup)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(
new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
new ObjectEncoder(),
new MPCClientHandler(partyId));
}
});
ChannelFuture future = bootstrap.connect(info.host, info.port).sync();
clientChannels.put(partyId, future.channel());
}
/**
* Party information
*/
public static class PartyInfo {
public final String host;
public final int port;
public final byte[] publicKey;
public PartyInfo(String host, int port, byte[] publicKey) {
this.host = host;
this.port = port;
this.publicKey = publicKey;
}
}
/**
* Network message wrapper
*/
public static class MPCMessage implements Serializable {
public final int senderId;
public final int recipientId;
public final int id;
public final Object content;
public MPCMessage(int senderId, int recipientId, int id, Object content) {
this.senderId = senderId;
this.recipientId = recipientId;
this.id = id;
this.content = content;
}
}
/**
* Server-side handler
*/
private class MPCServerHandler extends SimpleChannelInboundHandler<MPCMessage> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, MPCMessage msg) {
// Verify recipient
if (msg.recipientId != localPartyId) {
logger.warn("Received message intended for party {}, discarding",
msg.recipientId);
return;
}
// Queue message for processing
BlockingQueue<Object> queue = messageQueues.computeIfAbsent(
msg.senderId, k -> new LinkedBlockingQueue<>());
queue.add(msg.content);
logger.debug("Received message {} from party {}", msg.id, msg.senderId);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
logger.error("Server handler error", cause);
ctx.close();
}
}
/**
* Client-side handler
*/
private class MPCClientHandler extends SimpleChannelInboundHandler<MPCMessage> {
private final int remotePartyId;
public MPCClientHandler(int remotePartyId) {
this.remotePartyId = remotePartyId;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, MPCMessage msg) {
// Verify sender matches the party we connected to
if (msg.senderId != remotePartyId) {
logger.warn("Received message from unexpected party {} (expected {})",
msg.senderId, remotePartyId);
return;
}
// Verify recipient
if (msg.recipientId != localPartyId) {
logger.warn("Received message intended for party {}, discarding",
msg.recipientId);
return;
}
// Queue message
BlockingQueue<Object> queue = messageQueues.computeIfAbsent(
msg.senderId, k -> new LinkedBlockingQueue<>());
queue.add(msg.content);
logger.debug("Received message {} from party {}", msg.id, msg.senderId);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
logger.error("Client handler error for party {}", remotePartyId, cause);
ctx.close();
}
}
@Override
public void close() {
try {
for (Channel channel : clientChannels.values()) {
channel.close().sync();
}
serverChannel.close().sync();
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
logger.info("MPC Network closed");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
8. Malicious Security with Zero-Knowledge Proofs
package com.threshold.zk;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.*;
/**
* Zero-knowledge proofs for threshold signature security
*/
public class ZeroKnowledgeProofs {
private final ECPoint generator;
private final BigInteger order;
private final SecureRandom secureRandom;
public ZeroKnowledgeProofs(ECPoint generator, BigInteger order) {
this.generator = generator;
this.order = order;
this.secureRandom = new SecureRandom();
}
/**
* Proof of knowledge of discrete logarithm (Schnorr)
*/
public static class DLogProof {
public final ECPoint commitment;
public final BigInteger challenge;
public final BigInteger response;
public DLogProof(ECPoint commitment, BigInteger challenge, BigInteger response) {
this.commitment = commitment;
this.challenge = challenge;
this.response = response;
}
}
/**
* Prove knowledge of x such that Y = g^x
*/
public DLogProof proveDLog(BigInteger x) {
// Commitment: t = g^r
BigInteger r = generateRandomModOrder();
ECPoint t = generator.multiply(r);
// Challenge: c = H(t)
BigInteger c = hash(t);
// Response: s = r - c*x mod order
BigInteger s = r.subtract(c.multiply(x)).mod(order);
return new DLogProof(t, c, s);
}
/**
* Verify proof of discrete logarithm
*/
public boolean verifyDLog(DLogProof proof, ECPoint y) {
// Compute g^s * y^c
ECPoint left = generator.multiply(proof.response);
ECPoint right = y.multiply(proof.challenge);
ECPoint t = left.add(right);
// Verify challenge
BigInteger expectedC = hash(t);
return expectedC.equals(proof.challenge);
}
/**
* Proof that encrypted share is correct (for DKG)
*/
public static class ShareProof {
public final ECPoint commitment1;
public final ECPoint commitment2;
public final BigInteger challenge;
public final BigInteger response1;
public final BigInteger response2;
public ShareProof(ECPoint c1, ECPoint c2, BigInteger c,
BigInteger r1, BigInteger r2) {
this.commitment1 = c1;
this.commitment2 = c2;
this.challenge = c;
this.response1 = r1;
this.response2 = r2;
}
}
/**
* Prove that share corresponds to commitment and encrypted value
*/
public ShareProof proveShareCorrectness(BigInteger share,
BigInteger encryptionRandomness,
ECPoint publicKey,
ECPoint commitment) {
// This implements proof that:
// - share corresponds to commitment (g^share)
// - encrypted share is correctly formed
BigInteger r1 = generateRandomModOrder();
BigInteger r2 = generateRandomModOrder();
// Commitments
ECPoint A = generator.multiply(r1);
ECPoint B = publicKey.multiply(r1).add(generator.multiply(r2));
// Challenge
List<byte[]> data = new ArrayList<>();
data.add(A.getEncoded(true));
data.add(B.getEncoded(true));
data.add(publicKey.getEncoded(true));
data.add(commitment.getEncoded(true));
BigInteger challenge = hash(data);
// Responses
BigInteger s1 = r1.subtract(challenge.multiply(share)).mod(order);
BigInteger s2 = r2.subtract(challenge.multiply(encryptionRandomness)).mod(order);
return new ShareProof(A, B, challenge, s1, s2);
}
/**
* Verify share correctness proof
*/
public boolean verifyShareCorrectness(ShareProof proof,
ECPoint encryptedShare,
ECPoint publicKey,
ECPoint commitment) {
// Verify A = g^s1 * commitment^c
ECPoint left1 = generator.multiply(proof.response1)
.add(commitment.multiply(proof.challenge));
// Verify B = pk^s1 * g^s2 * encryptedShare^c
ECPoint left2 = publicKey.multiply(proof.response1)
.add(generator.multiply(proof.response2))
.add(encryptedShare.multiply(proof.challenge));
// Verify challenge
List<byte[]> data = new ArrayList<>();
data.add(left1.getEncoded(true));
data.add(left2.getEncoded(true));
data.add(publicKey.getEncoded(true));
data.add(commitment.getEncoded(true));
BigInteger expectedC = hash(data);
return expectedC.equals(proof.challenge);
}
/**
* Range proof for values (simplified)
*/
public static class RangeProof {
public final List<ECPoint> commitments;
public final BigInteger challenge;
public final List<BigInteger> responses;
public RangeProof(List<ECPoint> commitments, BigInteger c,
List<BigInteger> responses) {
this.commitments = commitments;
this.challenge = c;
this.responses = responses;
}
}
/**
* Prove that value is in range [0, 2^k - 1]
*/
public RangeProof proveRange(BigInteger value, int bits) {
// Simplified range proof using bit decomposition
List<BigInteger> bitsList = new ArrayList<>();
for (int i = 0; i < bits; i++) {
bitsList.add(value.testBit(i) ? BigInteger.ONE : BigInteger.ZERO);
}
List<BigInteger> randomness = new ArrayList<>();
List<ECPoint> commitments = new ArrayList<>();
for (int i = 0; i < bits; i++) {
BigInteger r = generateRandomModOrder();
randomness.add(r);
// Commitment = g^bit * h^r
ECPoint bitPoint = bitsList.get(i).equals(BigInteger.ONE) ?
generator : generator.multiply(BigInteger.ZERO);
commitments.add(bitPoint.add(generator.multiply(r)));
}
// Challenge
List<byte[]> data = new ArrayList<>();
for (ECPoint c : commitments) {
data.add(c.getEncoded(true));
}
BigInteger challenge = hash(data);
// Responses
List<BigInteger> responses = new ArrayList<>();
for (int i = 0; i < bits; i++) {
BigInteger r = randomness.get(i);
BigInteger s = r.subtract(challenge.multiply(bitsList.get(i))).mod(order);
responses.add(s);
}
return new RangeProof(commitments, challenge, responses);
}
/**
* Proof of correct multiplication (for threshold ECDSA)
*/
public static class MultiplicationProof {
public final ECPoint A;
public final ECPoint B;
public final BigInteger challenge;
public final BigInteger response1;
public final BigInteger response2;
public final BigInteger response3;
public MultiplicationProof(ECPoint a, ECPoint b, BigInteger c,
BigInteger r1, BigInteger r2, BigInteger r3) {
this.A = a;
this.B = b;
this.challenge = c;
this.response1 = r1;
this.response2 = r2;
this.response3 = r3;
}
}
/**
* Prove that C = g^(x*y) given X = g^x, Y = g^y
*/
public MultiplicationProof proveMultiplication(BigInteger x, BigInteger y,
ECPoint X, ECPoint Y) {
BigInteger alpha = generateRandomModOrder();
BigInteger beta = generateRandomModOrder();
BigInteger gamma = generateRandomModOrder();
ECPoint A = generator.multiply(alpha);
ECPoint B = X.multiply(beta).add(generator.multiply(gamma));
// Challenge
List<byte[]> data = new ArrayList<>();
data.add(A.getEncoded(true));
data.add(B.getEncoded(true));
data.add(X.getEncoded(true));
data.add(Y.getEncoded(true));
BigInteger challenge = hash(data);
BigInteger r1 = alpha.subtract(challenge.multiply(x)).mod(order);
BigInteger r2 = beta.subtract(challenge.multiply(y)).mod(order);
BigInteger r3 = gamma.subtract(challenge.multiply(x.multiply(y))).mod(order);
return new MultiplicationProof(A, B, challenge, r1, r2, r3);
}
/**
* Verifiable Secret Sharing proof
*/
public boolean verifyVSS(List<ECPoint> commitments, Map<Integer, BigInteger> shares) {
for (Map.Entry<Integer, BigInteger> entry : shares.entrySet()) {
int index = entry.getKey();
BigInteger share = entry.getValue();
// Verify g^share == product of C_i^(index^i)
ECPoint left = generator.multiply(share);
ECPoint right = commitments.get(0);
BigInteger power = BigInteger.valueOf(index);
for (int i = 1; i < commitments.size(); i++) {
right = right.add(commitments.get(i).multiply(power));
power = power.multiply(BigInteger.valueOf(index));
}
if (!left.equals(right)) {
return false;
}
}
return true;
}
private BigInteger generateRandomModOrder() {
return new BigInteger(order.bitLength(), secureRandom).mod(order);
}
private BigInteger hash(ECPoint point) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
return new BigInteger(1, digest.digest(point.getEncoded(true)));
} catch (Exception e) {
throw new RuntimeException("Hashing failed", e);
}
}
private BigInteger hash(List<byte[]> data) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
for (byte[] d : data) {
digest.update(d);
}
return new BigInteger(1, digest.digest()).mod(order);
} catch (Exception e) {
throw new RuntimeException("Hashing failed", e);
}
}
}
9. REST API for Threshold Signatures
package com.threshold.api;
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@RestController
@RequestMapping("/api/threshold")
public class ThresholdController {
private final ThresholdService thresholdService;
private final Map<String, SigningSession> activeSessions = new ConcurrentHashMap<>();
public ThresholdController(ThresholdService thresholdService) {
this.thresholdService = thresholdService;
}
@PostMapping("/keygen/initiate")
public ResponseEntity<KeygenResponse> initiateKeygen(
@RequestBody KeygenRequest request) {
try {
String sessionId = UUID.randomUUID().toString();
KeygenSession session = thresholdService.initiateKeygen(
sessionId,
request.getThreshold(),
request.getTotalParticipants(),
request.getParticipantId()
);
activeSessions.put(sessionId, session);
return ResponseEntity.ok(new KeygenResponse(
sessionId,
session.getRound1Message()
));
} catch (Exception e) {
return ResponseEntity.badRequest()
.body(new KeygenResponse(e.getMessage()));
}
}
@PostMapping("/keygen/round2")
public ResponseEntity<KeygenResponse> keygenRound2(
@RequestBody KeygenRound2Request request) {
try {
KeygenSession session = (KeygenSession) activeSessions.get(
request.getSessionId()
);
Map<Integer, Object> round2Messages = session.processRound2(
request.getRound1Messages()
);
return ResponseEntity.ok(new KeygenResponse(
request.getSessionId(),
round2Messages
));
} catch (Exception e) {
return ResponseEntity.badRequest()
.body(new KeygenResponse(e.getMessage()));
}
}
@PostMapping("/sign/initiate")
public ResponseEntity<SignResponse> initiateSigning(
@RequestBody SignRequest request) {
try {
String sessionId = UUID.randomUUID().toString();
SigningSession session = thresholdService.initiateSigning(
sessionId,
request.getMessage(),
request.getSigners(),
request.getPrivateShare()
);
activeSessions.put(sessionId, session);
return ResponseEntity.ok(new SignResponse(
sessionId,
session.getRound1Message()
));
} catch (Exception e) {
return ResponseEntity.badRequest()
.body(new SignResponse(e.getMessage()));
}
}
@PostMapping("/sign/round2")
public ResponseEntity<SignatureResponse> signingRound2(
@RequestBody SignRound2Request request) {
try {
SigningSession session = (SigningSession) activeSessions.get(
request.getSessionId()
);
Signature signature = session.processRound2(
request.getRound1Messages()
);
return ResponseEntity.ok(new SignatureResponse(
signature.encode()
));
} catch (Exception e) {
return ResponseEntity.badRequest()
.body(new SignatureResponse(e.getMessage()));
}
}
@PostMapping("/verify")
public ResponseEntity<VerifyResponse> verifySignature(
@RequestBody VerifyRequest request) {
try {
boolean valid = thresholdService.verify(
request.getMessage(),
request.getSignature(),
request.getPublicKey()
);
return ResponseEntity.ok(new VerifyResponse(valid));
} catch (Exception e) {
return ResponseEntity.badRequest()
.body(new VerifyResponse(false, e.getMessage()));
}
}
@PostMapping("/share/refresh")
public ResponseEntity<RefreshResponse> refreshShares(
@RequestBody RefreshRequest request) {
try {
Map<Integer, BigInteger> newShares = thresholdService.refreshShares(
request.getCurrentShares(),
request.getThreshold()
);
return ResponseEntity.ok(new RefreshResponse(newShares));
} catch (Exception e) {
return ResponseEntity.badRequest()
.body(new RefreshResponse(e.getMessage()));
}
}
// Request/Response classes
public static class KeygenRequest {
private int threshold;
private int totalParticipants;
private int participantId;
// getters and setters
}
public static class KeygenResponse {
private String sessionId;
private Object round1Message;
private String error;
public KeygenResponse(String sessionId, Object round1Message) {
this.sessionId = sessionId;
this.round1Message = round1Message;
}
public KeygenResponse(String error) {
this.error = error;
}
// getters
}
public static class SignRequest {
private byte[] message;
private Set<Integer> signers;
private BigInteger privateShare;
// getters and setters
}
public static class SignResponse {
private String sessionId;
private Object round1Message;
private String error;
public SignResponse(String sessionId, Object round1Message) {
this.sessionId = sessionId;
this.round1Message = round1Message;
}
public SignResponse(String error) {
this.error = error;
}
// getters
}
}
10. Configuration and Security
# application.yml threshold: signature: curve: secp256k1 default-threshold: 2 default-participants: 3 scheme: FROST # FROST, GG18, BLS network: host: localhost base-port: 8000 connection-timeout: 30s heartbeat-interval: 5s security: require-malicious-security: true proactive-refresh-hours: 24 max-session-age: 3600s key-storage: hsm # hsm, file, memory zk-proofs: enable: true range-proof-bits: 64 batch-verification: true monitoring: metrics-enabled: true trace-sessions: true alert-on-abort: true
Best Practices
1. Key Management
// Never store shares in memory longer than necessary
public void processSigning(BigInteger share) {
byte[] shareBytes = share.toByteArray();
try {
// Use secure memory
SecureMemory secureMemory = new SecureMemory(shareBytes);
// Process
} finally {
// Zeroize
Arrays.fill(shareBytes, (byte) 0);
}
}
2. Secure Communication
// Always use authenticated encryption for network messages
public class SecureChannel {
private final SecretKey sessionKey;
private final Mac mac;
public byte[] send(byte[] message) {
// Encrypt
byte[] ciphertext = encrypt(message);
// Add MAC
byte[] mac = computeMAC(ciphertext);
// Combine
return concat(ciphertext, mac);
}
}
3. Identifiable Abort
// Identify malicious parties
public class IdentifiableAbort {
public void handleAbort(SigningSession session, int abortingParty) {
// Log abort
logger.warn("Party {} aborted signing session {}", abortingParty, session.getId());
// Verify if abort was malicious
if (isMaliciousAbort(session, abortingParty)) {
// Add to blacklist
blacklistParty(abortingParty);
// Trigger alerts
alertService.sendAlert("Malicious party detected: " + abortingParty);
}
}
}
4. Proactive Security
// Regularly refresh shares
@Scheduled(fixedDelay = 24, timeUnit = TimeUnit.HOURS)
public void proactiveRefresh() {
for (KeyShare share : activeShares) {
ShareRefresh refresh = refreshService.generateRefresh(share);
// Distribute refresh shares
network.broadcast(refresh);
// Apply locally
share.refresh(refresh);
}
}
Conclusion
This comprehensive threshold signatures implementation provides:
- Multiple signature schemes: FROST, GG18 (ECDSA), BLS
- Secure multi-party computation with identifiable abort
- Zero-knowledge proofs for malicious security
- Distributed key generation with VSS
- Proactive security with share refreshing
- Secure networking for MPC communication
- Comprehensive API for integration
Key benefits:
- No single point of failure in key management
- Tamper-proof against malicious parties
- Auditable with zero-knowledge proofs
- Scalable to many participants
- Production-ready with monitoring and alerts
This setup enables secure distributed signing for blockchain wallets, certificate authorities, and any application requiring distributed trust.
Advanced Java Programming Concepts and Projects (Related to Java Programming)
Number Guessing Game in Java:
This project teaches how to build a simple number guessing game using Java. It combines random number generation, loops, and conditional statements to create an interactive program where users guess a number until they find the correct answer.
Read more: https://macronepal.com/blog/number-guessing-game-in-java-a-complete-guide/
HashMap Basics in Java:
HashMap is a collection class used to store data in key-value pairs. It allows fast retrieval of values using keys and is widely used when working with structured data that requires quick searching and updating.
Read more: https://macronepal.com/blog/hashmap-basics-in-java-a-complete-guide/
Date and Time in Java:
This topic explains how to work with dates and times in Java using built-in classes. It helps developers manage time-related data such as current date, formatting time, and calculating time differences.
Read more: https://macronepal.com/blog/date-and-time-in-java-a-complete-guide/
StringBuilder in Java:
StringBuilder is used to create and modify strings efficiently. Unlike regular strings, it allows changes without creating new objects, making programs faster when handling large or frequently changing text.
Read more: https://macronepal.com/blog/stringbuilder-in-java-a-complete-guide/
Packages in Java:
Packages help organize Java classes into groups, making programs easier to manage and maintain. They also help prevent naming conflicts and improve code structure in large applications.
Read more: https://macronepal.com/blog/packages-in-java-a-complete-guide/
Interfaces in Java:
Interfaces define a set of methods that classes must implement. They help achieve abstraction and support multiple inheritance in Java, making programs more flexible and organized.
Read more: https://macronepal.com/blog/interfaces-in-java-a-complete-guide/
Abstract Classes in Java:
Abstract classes are classes that cannot be instantiated directly and may contain both abstract and non-abstract methods. They are used as base classes to define common features for other classes.
Read more: https://macronepal.com/blog/abstract-classes-in-java-a-complete-guide/
Method Overriding in Java:
Method overriding occurs when a subclass provides its own version of a method already defined in its parent class. It supports runtime polymorphism and allows customized behavior in child classes.
Read more: https://macronepal.com/blog/method-overriding-in-java-a-complete-guide/
The This Keyword in Java:
The this keyword refers to the current object in a class. It is used to access instance variables, call constructors, and differentiate between class variables and parameters.
Read more: https://macronepal.com/blog/the-this-keyword-in-java-a-complete-guide/
Encapsulation in Java:
Encapsulation is an object-oriented concept that involves bundling data and methods into a single unit and restricting direct access to some components. It improves data security and program organization.
Read more: https://macronepal.com/blog/encapsulation-in-java-a-complete-guide/