Merge pull request #238 from devrandom/ecies

ECIES encryption
This commit is contained in:
Roman Mandeleil 2015-03-13 00:43:34 +02:00
commit edd5eb1694
2 changed files with 70 additions and 63 deletions

View File

@ -220,7 +220,12 @@ public class EthereumIESEngine
// Apply the MAC.
byte[] T = new byte[mac.getMacSize()];
mac.init(new KeyParameter(K2));
byte[] K2a = new byte[hash.getDigestSize()];
hash.reset();
hash.update(K2, 0, K2.length);
hash.doFinal(K2a, 0);
mac.init(new KeyParameter(K2a));
mac.update(IV, 0, IV.length);
mac.update(C, 0, C.length);
if (P2 != null)
{

View File

@ -9,14 +9,11 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.asn1.sec.SECNamedCurves;
import org.spongycastle.asn1.x9.X9ECParameters;
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
import org.spongycastle.crypto.BufferedBlockCipher;
import org.spongycastle.crypto.KeyGenerationParameters;
import org.spongycastle.crypto.*;
import org.spongycastle.crypto.agreement.ECDHBasicAgreement;
import org.spongycastle.crypto.digests.SHA256Digest;
import org.spongycastle.crypto.engines.AESFastEngine;
import org.spongycastle.crypto.generators.ECKeyPairGenerator;
import org.spongycastle.crypto.generators.KDF2BytesGenerator;
import org.spongycastle.crypto.macs.HMac;
import org.spongycastle.crypto.modes.SICBlockCipher;
import org.spongycastle.crypto.params.*;
@ -24,22 +21,23 @@ import org.spongycastle.math.ec.ECPoint;
import org.spongycastle.util.encoders.Hex;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.security.Security;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertTrue;
public class ECIESTest {
public static final int MAC_KEY_SIZE = 128;
public static final int KEY_SIZE = 128;
static Logger log = LoggerFactory.getLogger("test");
private static ECDomainParameters curve;
private static final String CIPHERTEXT1 = "042a851331790adacf6e64fcb19d0872fcdf1285a899a12cdc897da941816b0ea6485402aaf6c2e0a5d98ae3af1b05c68b307d1e0eb7a426a46f1617ba5b94f90b606eee3b5e9d2b527a9ee52cfa377bcd118b9390ed27ffe7d48e8155004375cae209012c3e057bb13a478a64a201d79ad4ae83";
private static final X9ECParameters IES_CURVE_PARAM = SECNamedCurves.getByName("secp256r1");
private static final BigInteger PRIVATE_KEY1 = new BigInteger("51134539186617376248226283012294527978458758538121566045626095875284492680246");
private static ECPoint pub(BigInteger d) throws Exception {
private static ECPoint pub(BigInteger d) {
return curve.getG().multiply(d);
}
@ -53,30 +51,78 @@ public class ECIESTest {
@Test
public void testKDF() {
ConcatKDFBytesGenerator kdf = new ConcatKDFBytesGenerator(new SHA256Digest());
kdf.init(new KDFParameters(new String("Hello").getBytes(), new byte[0]));
kdf.init(new KDFParameters("Hello".getBytes(), new byte[0]));
byte[] bytes = new byte[2];
kdf.generateBytes(bytes, 0, bytes.length);
assertArrayEquals(new byte[]{-66, -89}, bytes);
}
@Test
public void testDecryptTestVector() throws Throwable {
public void testDecryptTestVector() throws IOException, InvalidCipherTextException {
ECPoint pub1 = pub(PRIVATE_KEY1);
byte[] cipher = Hex.decode(CIPHERTEXT1);
byte[] ciphertext = Hex.decode(CIPHERTEXT1);
byte[] plaintext = decrypt(PRIVATE_KEY1, ciphertext);
assertArrayEquals(new byte[]{1,1,1}, plaintext);
}
@Test
public void testRoundTrip() throws InvalidCipherTextException, IOException {
ECPoint pub1 = pub(PRIVATE_KEY1);
byte[] plaintext = "Hello world".getBytes();
byte[] ciphertext = encrypt(pub1, plaintext);
byte[] plaintext1 = decrypt(PRIVATE_KEY1, ciphertext);
assertArrayEquals(plaintext, plaintext1);
}
public static byte[] decrypt(BigInteger prv, byte[] cipher) throws InvalidCipherTextException, IOException {
ByteArrayInputStream is = new ByteArrayInputStream(cipher);
byte[] ephemBytes = new byte[2*((curve.getCurve().getFieldSize()+7)/8) + 1];
is.read(ephemBytes);
ECPoint ephem = curve.getCurve().decodePoint(ephemBytes);
byte[] IV = new byte[MAC_KEY_SIZE/8];
byte[] IV = new byte[KEY_SIZE /8];
is.read(IV);
byte[] cipherBody = new byte[is.available()];
is.read(cipherBody);
byte[] plaintext = decrypt(ephem, PRIVATE_KEY1, IV, cipherBody);
assertArrayEquals(new byte[]{1,1,1}, plaintext);
EthereumIESEngine iesEngine = makeIESEngine(false, ephem, prv, IV);
byte[] message = iesEngine.processBlock(cipherBody, 0, cipherBody.length);
return message;
}
public static byte[] decrypt(ECPoint ephem, BigInteger prv, byte[] IV, byte[] cipher) throws Throwable {
public static byte[] encrypt(ECPoint toPub, byte[] plaintext) throws InvalidCipherTextException, IOException {
ECKeyPairGenerator eGen = new ECKeyPairGenerator();
SecureRandom random = new SecureRandom();
KeyGenerationParameters gParam = new ECKeyGenerationParameters(curve, random);
eGen.init(gParam);
byte[] IV = new byte[KEY_SIZE/8];
new SecureRandom().nextBytes(IV);
AsymmetricCipherKeyPair ephemPair = eGen.generateKeyPair();
BigInteger prv = ((ECPrivateKeyParameters)ephemPair.getPrivate()).getD();
ECPoint pub = ((ECPublicKeyParameters)ephemPair.getPublic()).getQ();
EthereumIESEngine iesEngine = makeIESEngine(true, toPub, prv, IV);
ECKeyGenerationParameters keygenParams = new ECKeyGenerationParameters(curve, random);
ECKeyPairGenerator generator = new ECKeyPairGenerator();
generator.init(keygenParams);
ECKeyPairGenerator gen = new ECKeyPairGenerator();
gen.init(new ECKeyGenerationParameters(ECKey.CURVE, random));
byte[] cipher = iesEngine.processBlock(plaintext, 0, plaintext.length);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write(pub.getEncoded(false));
bos.write(IV);
bos.write(cipher);
return bos.toByteArray();
}
private static EthereumIESEngine makeIESEngine(boolean isEncrypt, ECPoint pub, BigInteger prv, byte[] IV) {
AESFastEngine aesFastEngine = new AESFastEngine();
EthereumIESEngine iesEngine = new EthereumIESEngine(
@ -90,54 +136,10 @@ public class ECIESTest {
byte[] d = new byte[] {};
byte[] e = new byte[] {};
IESParameters p = new IESWithCipherParameters(d, e, MAC_KEY_SIZE, MAC_KEY_SIZE);
ParametersWithIV parametersWithIV =
new ParametersWithIV(p, IV);
IESParameters p = new IESWithCipherParameters(d, e, KEY_SIZE, KEY_SIZE);
ParametersWithIV parametersWithIV = new ParametersWithIV(p, IV);
iesEngine.init(false, new ECPrivateKeyParameters(prv, curve), new ECPublicKeyParameters(ephem, curve), parametersWithIV);
byte[] message = iesEngine.processBlock(cipher, 0, cipher.length);
return message;
}
public static byte[] encrypt(byte[] plaintext) throws Throwable {
AESFastEngine aesFastEngine = new AESFastEngine();
EthereumIESEngine iesEngine = new EthereumIESEngine(
new ECDHBasicAgreement(),
new KDF2BytesGenerator(new SHA256Digest()),
new HMac(new SHA256Digest()),
new SHA256Digest(),
new BufferedBlockCipher(new SICBlockCipher(aesFastEngine)));
byte[] d = new byte[] {};
byte[] e = new byte[] {};
IESParameters p = new IESWithCipherParameters(d, e, 256, MAC_KEY_SIZE);
ParametersWithIV parametersWithIV = new ParametersWithIV(p, new byte[256/8]);
ECKeyPairGenerator eGen = new ECKeyPairGenerator();
SecureRandom random = new SecureRandom();
KeyGenerationParameters gParam = new ECKeyGenerationParameters(ECKey.CURVE, random);
eGen.init(gParam);
AsymmetricCipherKeyPair p1 = eGen.generateKeyPair();
AsymmetricCipherKeyPair p2 = eGen.generateKeyPair();
ECKeyGenerationParameters keygenParams = new ECKeyGenerationParameters(ECKey.CURVE, random);
ECKeyPairGenerator generator = new ECKeyPairGenerator();
generator.init(keygenParams);
ECKeyPairGenerator gen = new ECKeyPairGenerator();
gen.init(new ECKeyGenerationParameters(ECKey.CURVE, random));
iesEngine.init(true, p1.getPrivate(), p2.getPublic(), parametersWithIV);
byte[] cipher = iesEngine.processBlock(plaintext, 0, plaintext.length);
return cipher;
iesEngine.init(isEncrypt, new ECPrivateKeyParameters(prv, curve), new ECPublicKeyParameters(pub, curve), parametersWithIV);
return iesEngine;
}
}