Started implementing encryption tests -- don't yet work
parent
c76724f599
commit
46de34fbfb
|
@ -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'
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
// 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);
|
||||
logger.info("Using BouncyCastle instead of native provider to support secp256k1 named curve");
|
||||
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(bouncyCastleProvider, 1);
|
||||
logger.info("Using BouncyCastle instead of native provider to support secp256k1 named curve");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GlobalCryptoSetup() {
|
||||
doSetup();
|
||||
}
|
||||
public GlobalCryptoSetup() { doSetup(); }
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
@ -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