Started implementing encryption tests -- don't yet work

crypto-primitives
Tal Moran 2015-11-22 18:09:51 +02:00
parent c76724f599
commit 46de34fbfb
7 changed files with 211 additions and 16 deletions

View File

@ -46,7 +46,7 @@ dependencies {
compile 'com.google.protobuf:protobuf-java:3.+'
// Crypto
compile 'org.factcenter.qilin:qilin:1.+'
compile 'org.factcenter.qilin:qilin:1.1+'
compile 'org.bouncycastle:bcprov-jdk15on:1.53'
compile 'org.bouncycastle:bcpkix-jdk15on:1.53'

View File

@ -2,6 +2,10 @@ package meerkat.crypto;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import java.io.IOException;
import java.util.Random;
import static meerkat.protobuf.Crypto.*;
/**
@ -14,8 +18,23 @@ public interface Encryption {
* @param rnd
* @return
*/
RerandomizableEncryptedMessage encrypt(Message plaintext, EncryptionRandomness rnd); // TODO: type of exception; throws
RerandomizableEncryptedMessage encrypt(Message plaintext, EncryptionRandomness rnd) throws IOException; // TODO: type of exception; throws
/**
* Rerandomize a ciphertext using the supplied randomness.
* @param msg
* @param rnd
* @return
* @throws InvalidProtocolBufferException
*/
RerandomizableEncryptedMessage rerandomize(RerandomizableEncryptedMessage msg, EncryptionRandomness rnd) throws InvalidProtocolBufferException;
/**
* Generate randomness compatible with {@link #encrypt(Message, EncryptionRandomness) and
* {@link #rerandomize(RerandomizableEncryptedMessage, EncryptionRandomness)}}.
* @param rand
* @return
*/
EncryptionRandomness generateRandomness(Random rand);
}

View File

@ -1,6 +1,7 @@
package meerkat.crypto.concrete;
import com.google.protobuf.ByteString;
import com.google.protobuf.CodedOutputStream;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import meerkat.crypto.Encryption;
@ -24,14 +25,17 @@ import qilin.primitives.PseudorandomGenerator;
import qilin.util.PRGRandom;
import qilin.util.Pair;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.security.spec.*;
import java.util.Random;
/**
* Created by talm on 17/11/15.
*/
public class ECElGamalEncryption implements Encryption {
public class ECElGamalEncryption extends GlobalCryptoSetup implements Encryption {
final Logger logger = LoggerFactory.getLogger(getClass());
public final static String KEY_ALGORITHM = "ECDH";
@ -74,8 +78,17 @@ public class ECElGamalEncryption implements Encryption {
@Override
public Crypto.RerandomizableEncryptedMessage encrypt(Message plaintext, Crypto.EncryptionRandomness rnd) {
byte[] msg = plaintext.toByteArray();
ECPoint encodedMsg = group.injectiveEncode(plaintext.toByteArray(), new PRGRandom(msg));
// We write the message using writeDelimited to so the length gets prepended.
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
plaintext.writeDelimitedTo(out);
} catch (IOException e) {
logger.error("Should never happen!", e);
throw new RuntimeException("Error in ByteArrayOutputStream!", e);
}
byte[] msg = out.toByteArray();
ECPoint encodedMsg = group.injectiveEncode(msg, new PRGRandom(msg));
BigInteger rndInt = BigIntegers.fromUnsignedByteArray(rnd.getData().toByteArray());
Pair<ECPoint,ECPoint> cipherText = elGamalPK.encrypt(encodedMsg, rndInt);
@ -108,4 +121,13 @@ public class ECElGamalEncryption implements Encryption {
.build().toByteString()
).build();
}
@Override
public Crypto.EncryptionRandomness generateRandomness(Random rand) {
BigInteger randomInt = new BigInteger(group.getCurveParams().getN().bitLength() - 1, rand);
Crypto.EncryptionRandomness retval = Crypto.EncryptionRandomness.newBuilder()
.setData(ByteString.copyFrom(BigIntegers.asUnsignedByteArray(randomInt))).build();
return retval;
}
}

View File

@ -4,6 +4,7 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.Provider;
import java.security.Security;
/**
@ -13,6 +14,7 @@ public class GlobalCryptoSetup {
final static Logger logger = LoggerFactory.getLogger(GlobalCryptoSetup.class);
static boolean loadedBouncyCastle = false;
static Provider bouncyCastleProvider;
public static boolean hasSecp256k1Curve() {
// For now we just check if the java version is at least 8
@ -22,17 +24,20 @@ public class GlobalCryptoSetup {
return ((major > 1) || ((major > 0) && (minor > 7)));
}
public static void doSetup() {
public static Provider getBouncyCastleProvider() { doSetup(); return bouncyCastleProvider; }
public static synchronized void doSetup() {
if (bouncyCastleProvider == null) {
bouncyCastleProvider = new BouncyCastleProvider();
// Make bouncycastle our default provider if we're running on a JVM version < 8
// (earlier version don't support the EC curve we use for signatures)
if (!hasSecp256k1Curve() && !loadedBouncyCastle) {
loadedBouncyCastle = true;
Security.insertProviderAt(new BouncyCastleProvider(), 1);
Security.insertProviderAt(bouncyCastleProvider, 1);
logger.info("Using BouncyCastle instead of native provider to support secp256k1 named curve");
}
}
}
public GlobalCryptoSetup() {
doSetup();
}
public GlobalCryptoSetup() { doSetup(); }
}

View File

@ -17,7 +17,7 @@ import static org.junit.Assert.assertTrue;
/**
* Created by talm on 12/11/15.
*/
public class TestECDSASignature {
public class ECDSASignatureTest {
public static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12";
public static String KEYFILE_PASSWORD = "secret";

View File

@ -0,0 +1,61 @@
package meerkat.crypto.concrete;
import meerkat.protobuf.BulletinBoardAPI;
import meerkat.protobuf.ConcreteCrypto;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.Voting;
import org.junit.Before;
import org.junit.Test;
import qilin.primitives.concrete.ECElGamal;
import qilin.primitives.concrete.ECGroup;
import java.math.BigInteger;
import java.util.Random;
/**
* Test class for {@link ECElGamalEncryption}
*/
public class ECElGamalEncryptionTest {
Random rand = new Random(0); // Insecure deterministic random for testing.
ECElGamal.SK key;
ECGroup group;
ECElGamalEncryption enc;
ConcreteCrypto.ElGamalPublicKey serializedPk;
@Before
public void setup() throws Exception {
group = new ECGroup("secp256k1");
BigInteger sk = ECElGamal.generateSecretKey(group, rand);
key = new ECElGamal.SK(group, sk);
serializedPk = ECElGamalUtils.serializePk(group, key);
enc = new ECElGamalEncryption();
enc.init(serializedPk);
}
@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();
Crypto.RerandomizableEncryptedMessage cipherText = enc.encrypt(msg, enc.generateRandomness(rand));
Voting.PlaintextBallot decrypted = ECElGamalUtils.decrypt(Voting.PlaintextBallot.class, key, group, cipherText);
}
}

View File

@ -0,0 +1,88 @@
package meerkat.crypto.concrete;
import com.google.protobuf.*;
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;
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.lang.reflect.*;
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
*/
public class ECElGamalUtils {
final static Logger logger = LoggerFactory.getLogger(ECElGamalUtils.class);
public final static String ENCRYPTION_KEY_ALGORITHM = "ECDH";
/**
* Serialize an El-Gamal public key into a form acceptable by {@link ECElGamalEncryption}
* @param pk
* @return
*/
public static ConcreteCrypto.ElGamalPublicKey serializePk(ECGroup group, ElGamal.PK<ECPoint> pk) {
ECPoint pkPoint = pk.getPK();
ECParameterSpec params = group.getCurveParams();
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(pkPoint, params);
try {
KeyFactory fact = KeyFactory.getInstance(ENCRYPTION_KEY_ALGORITHM,
GlobalCryptoSetup.getBouncyCastleProvider());
PublicKey javaPk = fact.generatePublic(pubKeySpec);
ConcreteCrypto.ElGamalPublicKey serializedPk = ConcreteCrypto.ElGamalPublicKey.newBuilder()
.setSubjectPublicKeyInfo(ByteString.copyFrom(javaPk.getEncoded())).build();
return serializedPk;
} catch (NoSuchAlgorithmException|InvalidKeySpecException e) {
logger.error("Should never happen!", e);
throw new RuntimeException("Error converting public key!", e);
}
}
/**
* Standard (non-threshold) decryption for testing purposes.
* @param secretKey
* @return
*/
public static <T extends Message> T decrypt(Class<T> plaintextMessageType, ECElGamal.SK secretKey, ECGroup group, Crypto.RerandomizableEncryptedMessage opaqueCipher)
throws InvalidProtocolBufferException {
ConcreteCrypto.ElGamalCiphertext cipherText = ConcreteCrypto.ElGamalCiphertext.parseFrom(opaqueCipher.getData());
ByteString c1encoded = cipherText.getC1();
ByteString c2encoded = cipherText.getC2();
ECPoint c1 = group.decode(c1encoded.toByteArray());
ECPoint c2 = group.decode(c2encoded.toByteArray());
ECPoint plaintextEncoded = secretKey.decrypt(new Pair<ECPoint, ECPoint>(c1, c2));
byte[] plaintext = group.injectiveDecode(plaintextEncoded);
CodedInputStream ci = CodedInputStream.newInstance(plaintext);
try {
java.lang.reflect.Method newBuilder = plaintextMessageType.getMethod("newBuilder");
GeneratedMessage.Builder builder = (GeneratedMessage.Builder) newBuilder.invoke(plaintextMessageType);
return (T) builder.mergeFrom(ci).build();
} catch (Exception e) {
logger.error("Error parsing incoming message", e);
throw new InvalidProtocolBufferException("Plaintext protobuf error");
}
}
}