From 984d7457c6475baf0b5d4d652f125e56a2e676e7 Mon Sep 17 00:00:00 2001 From: Tal Moran Date: Sun, 22 Nov 2015 23:24:27 +0200 Subject: [PATCH] Bug fixes; Encryption/Rerandomization tests pass --- meerkat-common/build.gradle | 2 + .../crypto/concrete/ECElGamalEncryption.java | 7 ++ .../concrete/ECElGamalEncryptionTest.java | 96 +++++++++++++++---- .../crypto/concrete/ECElGamalUtils.java | 15 +-- 4 files changed, 97 insertions(+), 23 deletions(-) diff --git a/meerkat-common/build.gradle b/meerkat-common/build.gradle index 2b48d73..d2fe0fd 100644 --- a/meerkat-common/build.gradle +++ b/meerkat-common/build.gradle @@ -152,6 +152,8 @@ task fatCapsule(type: FatCapsule){ *===================================*/ repositories { + + mavenLocal(); // Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral) maven { diff --git a/meerkat-common/src/main/java/meerkat/crypto/concrete/ECElGamalEncryption.java b/meerkat-common/src/main/java/meerkat/crypto/concrete/ECElGamalEncryption.java index d724ca5..8718dd7 100644 --- a/meerkat-common/src/main/java/meerkat/crypto/concrete/ECElGamalEncryption.java +++ b/meerkat-common/src/main/java/meerkat/crypto/concrete/ECElGamalEncryption.java @@ -22,6 +22,7 @@ import org.slf4j.LoggerFactory; import qilin.primitives.concrete.ECElGamal; import qilin.primitives.concrete.ECGroup; import qilin.primitives.PseudorandomGenerator; +import qilin.primitives.generic.ElGamal; import qilin.util.PRGRandom; import qilin.util.Pair; @@ -49,6 +50,12 @@ public class ECElGamalEncryption extends GlobalCryptoSetup implements Encryption ECGroup group; + public ECGroup getGroup() { return group; } + + public ECElGamal.PK getElGamalPK() { + return elGamalPK; + } + public void init(ConcreteCrypto.ElGamalPublicKey serializedPk) throws InvalidKeySpecException { AsymmetricKeyParameter keyParam; diff --git a/meerkat-common/src/test/java/meerkat/crypto/concrete/ECElGamalEncryptionTest.java b/meerkat-common/src/test/java/meerkat/crypto/concrete/ECElGamalEncryptionTest.java index ba1c54c..0d628e8 100644 --- a/meerkat-common/src/test/java/meerkat/crypto/concrete/ECElGamalEncryptionTest.java +++ b/meerkat-common/src/test/java/meerkat/crypto/concrete/ECElGamalEncryptionTest.java @@ -4,18 +4,32 @@ import meerkat.protobuf.BulletinBoardAPI; import meerkat.protobuf.ConcreteCrypto; import meerkat.protobuf.Crypto; import meerkat.protobuf.Voting; +import org.bouncycastle.math.ec.ECPoint; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import qilin.primitives.concrete.ECElGamal; import qilin.primitives.concrete.ECGroup; +import qilin.primitives.generic.ElGamal; +import qilin.util.Pair; import java.math.BigInteger; import java.util.Random; +import static org.junit.Assert.*; + /** * Test class for {@link ECElGamalEncryption} */ public class ECElGamalEncryptionTest { + final Logger logger = LoggerFactory.getLogger(getClass()); + /** + * Number of times to repeat probabilistic tests. + */ + public final static int CONFIDENCE = 10; + Random rand = new Random(0); // Insecure deterministic random for testing. ECElGamal.SK key; @@ -37,25 +51,75 @@ public class ECElGamalEncryptionTest { enc.init(serializedPk); } + + Voting.PlaintextBallot genRandomBallot(int numQuestions, int numAnswers, int maxAnswer) { + Voting.PlaintextBallot.Builder ballot = Voting.PlaintextBallot.newBuilder(); + ballot.setSerialNumber(rand.nextInt(1000000)); + for (int i = 0; i < numQuestions; ++i) { + Voting.BallotAnswer.Builder answers = ballot.addAnswersBuilder(); + for (int j = 0; j < numAnswers; ++j) { + answers.addAnswer(rand.nextInt(maxAnswer)); + } + } + return ballot.build(); + } + + /** + * Testing just the key management + * @throws Exception + */ @Test - public void testEncrypt() throws Exception { - Voting.PlaintextBallot msg = Voting.PlaintextBallot.newBuilder() - .setSerialNumber(1) - .addAnswers( Voting.BallotAnswer.newBuilder() - .addAnswer(2) - .addAnswer(3) - .addAnswer(0) - ) - .addAnswers( Voting.BallotAnswer.newBuilder() - .addAnswer(5) - .addAnswer(6) - .addAnswer(7) - ) - .build(); + public void testPkSerialization() throws Exception { + ECElGamal.PK pk = enc.getElGamalPK(); - Crypto.RerandomizableEncryptedMessage cipherText = enc.encrypt(msg, enc.generateRandomness(rand)); + ECPoint point = enc.getGroup().sample(rand); + Pair cipher = pk.encrypt(point, pk.getRandom(rand)); - Voting.PlaintextBallot decrypted = ECElGamalUtils.decrypt(Voting.PlaintextBallot.class, key, group, cipherText); + ECPoint decrypted = key.decrypt(cipher); + assertEquals("Decrypted value not equal to encrypted value!", point, decrypted); + } + + @Test + public void testEncryption() throws Exception { + for (int i = 0; i < CONFIDENCE; ++i) { + Voting.PlaintextBallot msg = genRandomBallot(2,3,16); // 2 questions with 3 answers each, in range 0-15. + if (msg.getSerializedSize() > enc.getGroup().getInjectiveEncodeMsgLength()) { + logger.error("Test Message too big (|msg|={} > max={}), expect failure.", + msg.getSerializedSize(), enc.getGroup().getInjectiveEncodeMsgLength()); + } + + Crypto.RerandomizableEncryptedMessage cipherText = enc.encrypt(msg, enc.generateRandomness(rand)); + + Voting.PlaintextBallot decrypted = ECElGamalUtils.decrypt(Voting.PlaintextBallot.class, key, group, cipherText); + + assertEquals("Decrypted value differs from encrypted value (i="+i+")!", msg, decrypted); + } + } + + @Test + public void testRerandomizeModifiesCiphertext() throws Exception { + Voting.PlaintextBallot msg = genRandomBallot(2,3,16); // 2 questions with 3 answers each, in range 0-15. + Crypto.RerandomizableEncryptedMessage cipher1 = enc.encrypt(msg, enc.generateRandomness(rand)); + Crypto.RerandomizableEncryptedMessage cipher2 = enc.rerandomize(cipher1, enc.generateRandomness(rand)); + assertNotEquals("Rerandomized cipher identical to original!", cipher1, cipher2); + } + + @Test + public void testRerandomizePreservesPlaintext() throws Exception { + for (int i = 0; i < CONFIDENCE; ++i) { + Voting.PlaintextBallot msg = genRandomBallot(2,3,16); // 2 questions with 3 answers each, in range 0-15. + + Crypto.RerandomizableEncryptedMessage cipher = enc.encrypt(msg, enc.generateRandomness(rand)); + Crypto.RerandomizableEncryptedMessage cipher2 = cipher; + for (int j = 0; j < CONFIDENCE; ++j) + cipher2 = enc.rerandomize(cipher2, enc.generateRandomness(rand)); + + Voting.PlaintextBallot decrypted = ECElGamalUtils.decrypt(Voting.PlaintextBallot.class, key, group, + cipher2); + + assertEquals("Decrypted value differs from original encrypted value (i="+i+")!", msg, decrypted); + } } } + diff --git a/meerkat-common/src/test/java/meerkat/crypto/concrete/ECElGamalUtils.java b/meerkat-common/src/test/java/meerkat/crypto/concrete/ECElGamalUtils.java index 3a43cdb..fec4803 100644 --- a/meerkat-common/src/test/java/meerkat/crypto/concrete/ECElGamalUtils.java +++ b/meerkat-common/src/test/java/meerkat/crypto/concrete/ECElGamalUtils.java @@ -1,9 +1,11 @@ package meerkat.crypto.concrete; -import com.google.protobuf.*; +import com.google.protobuf.ByteString; +import com.google.protobuf.GeneratedMessage; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.Message; import meerkat.protobuf.ConcreteCrypto; import meerkat.protobuf.Crypto; -import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.spec.ECParameterSpec; import org.bouncycastle.jce.spec.ECPublicKeySpec; import org.bouncycastle.math.ec.ECPoint; @@ -14,13 +16,11 @@ import qilin.primitives.concrete.ECGroup; import qilin.primitives.generic.ElGamal; import qilin.util.Pair; -import java.lang.reflect.*; +import java.io.ByteArrayInputStream; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; -import java.util.Random; /** * utilities for ECElgamal @@ -73,12 +73,13 @@ public class ECElGamalUtils { byte[] plaintext = group.injectiveDecode(plaintextEncoded); - CodedInputStream ci = CodedInputStream.newInstance(plaintext); + ByteArrayInputStream in = new ByteArrayInputStream(plaintext); try { java.lang.reflect.Method newBuilder = plaintextMessageType.getMethod("newBuilder"); GeneratedMessage.Builder builder = (GeneratedMessage.Builder) newBuilder.invoke(plaintextMessageType); - return (T) builder.mergeFrom(ci).build(); + builder.mergeDelimitedFrom(in); + return plaintextMessageType.cast(builder.build()); } catch (Exception e) { logger.error("Error parsing incoming message", e); throw new InvalidProtocolBufferException("Plaintext protobuf error");