package org.bouncycastle.crypto.commitments;

import java.security.SecureRandom;

import org.bouncycastle.crypto.Commitment;
import org.bouncycastle.crypto.Committer;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.ExtendedDigest;
import org.bouncycastle.util.Arrays;

A basic hash-committer based on the one described in "Making Mix Nets Robust for Electronic Voting by Randomized Partial Checking", by Jakobsson, Juels, and Rivest (11th Usenix Security Symposium, 2002).

The algorithm used by this class differs from the one given in that it includes the length of the message in the hash calculation.

/** * A basic hash-committer based on the one described in "Making Mix Nets Robust for Electronic Voting by Randomized Partial Checking", * by Jakobsson, Juels, and Rivest (11th Usenix Security Symposium, 2002). * <p> * The algorithm used by this class differs from the one given in that it includes the length of the message in the hash calculation. * </p> */
public class GeneralHashCommitter implements Committer { private final Digest digest; private final int byteLength; private final SecureRandom random;
Base Constructor. The maximum message length that can be committed to is half the length of the internal block size for the digest (ExtendedDigest.getBlockLength()).
Params:
  • digest – digest to use for creating commitments.
  • random – source of randomness for generating secrets.
/** * Base Constructor. The maximum message length that can be committed to is half the length of the internal * block size for the digest (ExtendedDigest.getBlockLength()). * * @param digest digest to use for creating commitments. * @param random source of randomness for generating secrets. */
public GeneralHashCommitter(ExtendedDigest digest, SecureRandom random) { this.digest = digest; this.byteLength = digest.getByteLength(); this.random = random; }
Generate a commitment for the passed in message.
Params:
  • message – the message to be committed to,
Returns:a Commitment
/** * Generate a commitment for the passed in message. * * @param message the message to be committed to, * @return a Commitment */
public Commitment commit(byte[] message) { if (message.length > byteLength / 2) { throw new DataLengthException("Message to be committed to too large for digest."); } byte[] w = new byte[byteLength - message.length]; random.nextBytes(w); return new Commitment(w, calculateCommitment(w, message)); }
Return true if the passed in commitment represents a commitment to the passed in message.
Params:
  • commitment – a commitment previously generated.
  • message – the message that was expected to have been committed to.
Returns:true if commitment matches message, false otherwise.
/** * Return true if the passed in commitment represents a commitment to the passed in message. * * @param commitment a commitment previously generated. * @param message the message that was expected to have been committed to. * @return true if commitment matches message, false otherwise. */
public boolean isRevealed(Commitment commitment, byte[] message) { if (message.length + commitment.getSecret().length != byteLength) { throw new DataLengthException("Message and witness secret lengths do not match."); } byte[] calcCommitment = calculateCommitment(commitment.getSecret(), message); return Arrays.constantTimeAreEqual(commitment.getCommitment(), calcCommitment); } private byte[] calculateCommitment(byte[] w, byte[] message) { byte[] commitment = new byte[digest.getDigestSize()]; digest.update(w, 0, w.length); digest.update(message, 0, message.length); digest.update((byte)((message.length >>> 8))); digest.update((byte)(message.length)); digest.doFinal(commitment, 0); return commitment; } }