Started implementing encryption tests -- don't yet work
parent
c76724f599
commit
46de34fbfb
|
@ -46,7 +46,7 @@ dependencies {
|
||||||
compile 'com.google.protobuf:protobuf-java:3.+'
|
compile 'com.google.protobuf:protobuf-java:3.+'
|
||||||
|
|
||||||
// Crypto
|
// Crypto
|
||||||
compile 'org.factcenter.qilin:qilin:1.+'
|
compile 'org.factcenter.qilin:qilin:1.1+'
|
||||||
compile 'org.bouncycastle:bcprov-jdk15on:1.53'
|
compile 'org.bouncycastle:bcprov-jdk15on:1.53'
|
||||||
compile 'org.bouncycastle:bcpkix-jdk15on:1.53'
|
compile 'org.bouncycastle:bcpkix-jdk15on:1.53'
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,10 @@ package meerkat.crypto;
|
||||||
|
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
import com.google.protobuf.Message;
|
import com.google.protobuf.Message;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
import static meerkat.protobuf.Crypto.*;
|
import static meerkat.protobuf.Crypto.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,8 +18,23 @@ public interface Encryption {
|
||||||
* @param rnd
|
* @param rnd
|
||||||
* @return
|
* @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;
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package meerkat.crypto.concrete;
|
package meerkat.crypto.concrete;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
|
import com.google.protobuf.CodedOutputStream;
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
import com.google.protobuf.Message;
|
import com.google.protobuf.Message;
|
||||||
import meerkat.crypto.Encryption;
|
import meerkat.crypto.Encryption;
|
||||||
|
@ -24,14 +25,17 @@ import qilin.primitives.PseudorandomGenerator;
|
||||||
import qilin.util.PRGRandom;
|
import qilin.util.PRGRandom;
|
||||||
import qilin.util.Pair;
|
import qilin.util.Pair;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.security.SecureRandom;
|
||||||
import java.security.spec.*;
|
import java.security.spec.*;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by talm on 17/11/15.
|
* 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());
|
final Logger logger = LoggerFactory.getLogger(getClass());
|
||||||
|
|
||||||
public final static String KEY_ALGORITHM = "ECDH";
|
public final static String KEY_ALGORITHM = "ECDH";
|
||||||
|
@ -74,8 +78,17 @@ public class ECElGamalEncryption implements Encryption {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Crypto.RerandomizableEncryptedMessage encrypt(Message plaintext, Crypto.EncryptionRandomness rnd) {
|
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());
|
BigInteger rndInt = BigIntegers.fromUnsignedByteArray(rnd.getData().toByteArray());
|
||||||
Pair<ECPoint,ECPoint> cipherText = elGamalPK.encrypt(encodedMsg, rndInt);
|
Pair<ECPoint,ECPoint> cipherText = elGamalPK.encrypt(encodedMsg, rndInt);
|
||||||
|
@ -108,4 +121,13 @@ public class ECElGamalEncryption implements Encryption {
|
||||||
.build().toByteString()
|
.build().toByteString()
|
||||||
).build();
|
).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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.security.Provider;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,6 +14,7 @@ public class GlobalCryptoSetup {
|
||||||
final static Logger logger = LoggerFactory.getLogger(GlobalCryptoSetup.class);
|
final static Logger logger = LoggerFactory.getLogger(GlobalCryptoSetup.class);
|
||||||
|
|
||||||
static boolean loadedBouncyCastle = false;
|
static boolean loadedBouncyCastle = false;
|
||||||
|
static Provider bouncyCastleProvider;
|
||||||
|
|
||||||
public static boolean hasSecp256k1Curve() {
|
public static boolean hasSecp256k1Curve() {
|
||||||
// For now we just check if the java version is at least 8
|
// 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)));
|
return ((major > 1) || ((major > 0) && (minor > 7)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void doSetup() {
|
public static Provider getBouncyCastleProvider() { doSetup(); return 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)
|
public static synchronized void doSetup() {
|
||||||
if (!hasSecp256k1Curve() && !loadedBouncyCastle) {
|
if (bouncyCastleProvider == null) {
|
||||||
loadedBouncyCastle = true;
|
bouncyCastleProvider = new BouncyCastleProvider();
|
||||||
Security.insertProviderAt(new BouncyCastleProvider(), 1);
|
// Make bouncycastle our default provider if we're running on a JVM version < 8
|
||||||
logger.info("Using BouncyCastle instead of native provider to support secp256k1 named curve");
|
// (earlier version don't support the EC curve we use for signatures)
|
||||||
|
if (!hasSecp256k1Curve() && !loadedBouncyCastle) {
|
||||||
|
loadedBouncyCastle = true;
|
||||||
|
Security.insertProviderAt(bouncyCastleProvider, 1);
|
||||||
|
logger.info("Using BouncyCastle instead of native provider to support secp256k1 named curve");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlobalCryptoSetup() {
|
public GlobalCryptoSetup() { doSetup(); }
|
||||||
doSetup();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ import static org.junit.Assert.assertTrue;
|
||||||
/**
|
/**
|
||||||
* Created by talm on 12/11/15.
|
* 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_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12";
|
||||||
public static String KEYFILE_PASSWORD = "secret";
|
public static String KEYFILE_PASSWORD = "secret";
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue