Utilities/test/dorkbox/util/crypto/EccTest.java

333 lines
13 KiB
Java

/*
* Copyright 2015 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.util.crypto;
import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Arrays;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.BasicAgreement;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.agreement.ECDHCBasicAgreement;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.engines.IESEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.IESParameters;
import org.bouncycastle.crypto.params.IESWithCipherParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.signers.ECDSASigner;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.crypto.util.PublicKeyFactory;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.util.encoders.Hex;
import org.junit.Test;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import dorkbox.serializers.bouncycastle.EccPrivateKeySerializer;
import dorkbox.serializers.bouncycastle.EccPublicKeySerializer;
import dorkbox.serializers.bouncycastle.IesParametersSerializer;
import dorkbox.serializers.bouncycastle.IesWithCipherParametersSerializer;
public class EccTest {
org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(this.getClass());
private static String entropySeed = "asdjhaffasttjasdasdgfgaerym0698768.,./8909087s0dfgkjgb49bmngrSGDSG#";
@Test
public void EccStreamMode() throws IOException {
SecureRandom secureRandom = new SecureRandom();
AsymmetricCipherKeyPair key1 = CryptoECC.generateKeyPair(CryptoECC.default_curve, secureRandom);
AsymmetricCipherKeyPair key2 = CryptoECC.generateKeyPair(CryptoECC.default_curve, secureRandom);
IESParameters cipherParams = CryptoECC.generateSharedParameters(secureRandom);
IESEngine encrypt = CryptoECC.createEngine();
IESEngine decrypt = CryptoECC.createEngine();
// note: we want an ecc key that is AT LEAST 512 bits! (which is equal to AES 256)
// using 521 bits from curve.
CipherParameters private1 = key1.getPrivate();
CipherParameters public1 = key1.getPublic();
CipherParameters private2 = key2.getPrivate();
CipherParameters public2 = key2.getPublic();
byte[] message = Hex.decode(
"123456784358754934597967249867359283792374987692348750276509765091834790abcdef123456784358754934597967249867359283792374987692348750276509765091834790abcdef123456784358754934597967249867359283792374987692348750276509765091834790abcdef");
// test stream mode
byte[] encrypted = CryptoECC.encrypt(encrypt, private1, public2, cipherParams, message, logger);
byte[] plaintext = CryptoECC.decrypt(decrypt, private2, public1, cipherParams, encrypted, logger);
if (Arrays.equals(encrypted, message)) {
fail("stream cipher test failed");
}
if (!Arrays.equals(plaintext, message)) {
fail("stream cipher test failed");
}
}
@Test
public void EccAesMode() throws IOException {
// test AES encrypt mode
SecureRandom secureRandom = new SecureRandom();
AsymmetricCipherKeyPair key1 = CryptoECC.generateKeyPair(CryptoECC.default_curve, secureRandom);
AsymmetricCipherKeyPair key2 = CryptoECC.generateKeyPair(CryptoECC.default_curve, secureRandom);
PaddedBufferedBlockCipher aesEngine1 = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
PaddedBufferedBlockCipher aesEngine2 = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
IESWithCipherParameters cipherParams = CryptoECC.generateSharedParametersWithCipher(secureRandom);
IESEngine encrypt = CryptoECC.createEngine(aesEngine1);
IESEngine decrypt = CryptoECC.createEngine(aesEngine2);
// note: we want an ecc key that is AT LEAST 512 bits! (which is equal to AES 256)
// using 521 bits from curve.
CipherParameters private1 = key1.getPrivate();
CipherParameters public1 = key1.getPublic();
CipherParameters private2 = key2.getPrivate();
CipherParameters public2 = key2.getPublic();
byte[] message = Hex.decode("123456784358754934597967249867359283792374987692348750276509765091834790abcdef123456784358754934597967249867359283792374987692348750276509765091834790abcdef123456784358754934597967249867359283792374987692348750276509765091834790abcdef");
// test stream mode
byte[] encrypted = CryptoECC.encrypt(encrypt, private1, public2, cipherParams, message, logger);
byte[] plaintext = CryptoECC.decrypt(decrypt, private2, public1, cipherParams, encrypted, logger);
if (Arrays.equals(encrypted, message)) {
fail("stream cipher test failed");
}
if (!Arrays.equals(plaintext, message)) {
fail("stream cipher test failed");
}
}
@Test
public void Ecdh() throws IOException {
// test DH key exchange
SecureRandom secureRandom = new SecureRandom();
AsymmetricCipherKeyPair key1 = CryptoECC.generateKeyPair(CryptoECC.default_curve, secureRandom);
AsymmetricCipherKeyPair key2 = CryptoECC.generateKeyPair(CryptoECC.default_curve, secureRandom);
BasicAgreement e1 = new ECDHCBasicAgreement();
BasicAgreement e2 = new ECDHCBasicAgreement();
e1.init(key1.getPrivate());
e2.init(key2.getPrivate());
BigInteger k1 = e1.calculateAgreement(key2.getPublic());
BigInteger k2 = e2.calculateAgreement(key1.getPublic());
if (!k1.equals(k2)) {
fail("ECDHC cipher test failed");
}
}
@Test
public void EccDsa() throws IOException {
SecureRandom secureRandom = new SecureRandom();
AsymmetricCipherKeyPair key1 = CryptoECC.generateKeyPair(CryptoECC.default_curve, secureRandom);
ParametersWithRandom param = new ParametersWithRandom(key1.getPrivate(), new SecureRandom());
ECDSASigner ecdsa = new ECDSASigner();
ecdsa.init(true, param);
byte[] message = new BigInteger("345234598734987394672039478602934578").toByteArray();
BigInteger[] sig = ecdsa.generateSignature(message);
ecdsa.init(false, key1.getPublic());
if (!ecdsa.verifySignature(message, sig[0], sig[1])) {
fail("ECDSA signature fails");
}
}
@Test
public void EccSerialization() {
SecureRandom secureRandom = new SecureRandom();
AsymmetricCipherKeyPair key1 = CryptoECC.generateKeyPair(CryptoECC.default_curve, secureRandom);
IESParameters cipherAParams = CryptoECC.generateSharedParameters(secureRandom);
IESWithCipherParameters cipherBParams = CryptoECC.generateSharedParametersWithCipher(secureRandom);
// note: we want an ecc key that is AT LEAST 512 bits! (which is equal to AES 256)
// using 521 bits from curve.
ECPrivateKeyParameters private1 = (ECPrivateKeyParameters) key1.getPrivate();
ECPublicKeyParameters public1 = (ECPublicKeyParameters) key1.getPublic();
Kryo kryo = new Kryo();
kryo.register(IESParameters.class, new IesParametersSerializer());
kryo.register(IESWithCipherParameters.class, new IesWithCipherParametersSerializer());
kryo.register(ECPublicKeyParameters.class, new EccPublicKeySerializer());
kryo.register(ECPrivateKeyParameters.class, new EccPrivateKeySerializer());
// Test output to stream, large buffer.
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
Output output = new Output(outStream, 4096);
kryo.writeClassAndObject(output, cipherAParams);
output.flush();
// Test input from stream, large buffer.
Input input = new Input(new ByteArrayInputStream(outStream.toByteArray()), 4096);
IESParameters cipherAParams2 = (IESParameters) kryo.readClassAndObject(input);
if (!CryptoECC.compare(cipherAParams, cipherAParams2)) {
fail("cipher parameters not equal");
}
// Test output to stream, large buffer.
outStream = new ByteArrayOutputStream();
output = new Output(outStream, 4096);
kryo.writeClassAndObject(output, cipherBParams);
output.flush();
// Test input from stream, large buffer.
input = new Input(new ByteArrayInputStream(outStream.toByteArray()), 4096);
IESWithCipherParameters cipherBParams2 = (IESWithCipherParameters) kryo.readClassAndObject(input);
if (!CryptoECC.compare(cipherBParams, cipherBParams2)) {
fail("cipher parameters not equal");
}
// Test output to stream, large buffer.
outStream = new ByteArrayOutputStream();
output = new Output(outStream, 4096);
kryo.writeClassAndObject(output, private1);
output.flush();
// Test input from stream, large buffer.
input = new Input(new ByteArrayInputStream(outStream.toByteArray()), 4096);
ECPrivateKeyParameters private2 = (ECPrivateKeyParameters) kryo.readClassAndObject(input);
if (!CryptoECC.compare(private1, private2)) {
fail("private keys not equal");
}
// Test output to stream, large buffer.
outStream = new ByteArrayOutputStream();
output = new Output(outStream, 4096);
kryo.writeClassAndObject(output, public1);
output.flush();
// Test input from stream, large buffer.
input = new Input(new ByteArrayInputStream(outStream.toByteArray()), 4096);
ECPublicKeyParameters public2 = (ECPublicKeyParameters) kryo.readClassAndObject(input);
if (!CryptoECC.compare(public1, public2)) {
fail("public keys not equal");
}
}
@Test
public void EccJceSerialization() throws IOException {
AsymmetricCipherKeyPair generateKeyPair = CryptoECC.generateKeyPair(CryptoECC.default_curve, new SecureRandom());
ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) generateKeyPair.getPrivate();
ECPublicKeyParameters publicKey = (ECPublicKeyParameters) generateKeyPair.getPublic();
BCECPublicKey bcecPublicKey = new BCECPublicKey("EC", publicKey, (ECParameterSpec) null, BouncyCastleProvider.CONFIGURATION);
byte[] publicBytes = bcecPublicKey.getEncoded();
// relies on the BC public key.
BCECPrivateKey bcecPrivateKey = new BCECPrivateKey("EC", privateKey, bcecPublicKey, (ECParameterSpec) null, BouncyCastleProvider.CONFIGURATION);
byte[] privateBytes = bcecPrivateKey.getEncoded();
ECPublicKeyParameters publicKey2 = (ECPublicKeyParameters) PublicKeyFactory.createKey(publicBytes);
ECPrivateKeyParameters privateKey2 = (ECPrivateKeyParameters) PrivateKeyFactory.createKey(privateBytes);
// test via signing
byte[] bytes = "hello, my name is inigo montoya".getBytes();
BigInteger[] signature = CryptoECC.generateSignature("SHA384", privateKey, new SecureRandom(entropySeed.getBytes()), bytes);
boolean verify1 = CryptoECC.verifySignature("SHA384", publicKey, bytes, signature);
if (!verify1) {
fail("failed signature verification");
}
boolean verify2 = CryptoECC.verifySignature("SHA384", publicKey2, bytes, signature);
if (!verify2) {
fail("failed signature verification");
}
// now reverse who signs what.
BigInteger[] signatureB = CryptoECC.generateSignature("SHA384", privateKey2, new SecureRandom(entropySeed.getBytes()), bytes);
boolean verifyB1 = CryptoECC.verifySignature("SHA384", publicKey, bytes, signatureB);
if (!verifyB1) {
fail("failed signature verification");
}
boolean verifyB2 = CryptoECC.verifySignature("SHA384", publicKey2, bytes, signatureB);
if (!verifyB2) {
fail("failed signature verification");
}
}
}