/*
* Copyright (c) [2016] [ <ether.camp> ]
* This file is part of the ethereumJ library.
*
* The ethereumJ library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The ethereumJ library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the ethereumJ library. If not, see <http://www.gnu.org/licenses/>.
*/
package org.ethereum.crypto;
import com.google.common.base.Throwables;
import org.ethereum.ConcatKDFBytesGenerator;
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
import org.spongycastle.crypto.BufferedBlockCipher;
import org.spongycastle.crypto.InvalidCipherTextException;
import org.spongycastle.crypto.KeyGenerationParameters;
import org.spongycastle.crypto.agreement.ECDHBasicAgreement;
import org.spongycastle.crypto.digests.SHA1Digest;
import org.spongycastle.crypto.digests.SHA256Digest;
import org.spongycastle.crypto.engines.AESFastEngine;
import org.spongycastle.crypto.generators.ECKeyPairGenerator;
import org.spongycastle.crypto.generators.EphemeralKeyPairGenerator;
import org.spongycastle.crypto.macs.HMac;
import org.spongycastle.crypto.modes.SICBlockCipher;
import org.spongycastle.crypto.params.*;
import org.spongycastle.crypto.parsers.ECIESPublicKeyParser;
import org.spongycastle.math.ec.ECPoint;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.SecureRandom;
import static org.ethereum.crypto.ECKey.CURVE;
public class ECIESCoder {
public static final int KEY_SIZE = 128;
public static byte[] decrypt(BigInteger privKey, byte[] cipher) throws IOException, InvalidCipherTextException {
return decrypt(privKey, cipher, null);
}
public static byte[] decrypt(BigInteger privKey, byte[] cipher, byte[] macData) throws IOException, InvalidCipherTextException {
byte[] plaintext;
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[KEY_SIZE /8];
is.read(IV);
byte[] cipherBody = new byte[is.available()];
is.read(cipherBody);
plaintext = decrypt(ephem, privKey, IV, cipherBody, macData);
return plaintext;
}
public static byte[] decrypt(ECPoint ephem, BigInteger prv, byte[] IV, byte[] cipher, byte[] macData) throws InvalidCipherTextException {
AESFastEngine aesFastEngine = new AESFastEngine();
EthereumIESEngine iesEngine = new EthereumIESEngine(
new ECDHBasicAgreement(),
new ConcatKDFBytesGenerator(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, KEY_SIZE, KEY_SIZE);
ParametersWithIV parametersWithIV =
new ParametersWithIV(p, IV);
iesEngine.init(false, new ECPrivateKeyParameters(prv, CURVE), new ECPublicKeyParameters(ephem, CURVE), parametersWithIV);
return iesEngine.processBlock(cipher, 0, cipher.length, macData);
}
/**
* Encryption equivalent to the Crypto++ default ECIES<ECP> settings:
*
* DL_KeyAgreementAlgorithm: DL_KeyAgreementAlgorithm_DH<struct ECPPoint,struct EnumToType<enum CofactorMultiplicationOption,0> >
* DL_KeyDerivationAlgorithm: DL_KeyDerivationAlgorithm_P1363<struct ECPPoint,0,class P1363_KDF2<class SHA1> >
* DL_SymmetricEncryptionAlgorithm: DL_EncryptionAlgorithm_Xor<class HMAC<class SHA1>,0>
* DL_PrivateKey: DL_Key<ECPPoint>
* DL_PrivateKey_EC<class ECP>
*
* Used for Whisper V3
*/
public static byte[] decryptSimple(BigInteger privKey, byte[] cipher) throws IOException, InvalidCipherTextException {
EthereumIESEngine iesEngine = new EthereumIESEngine(
new ECDHBasicAgreement(),
new MGF1BytesGeneratorExt(new SHA1Digest(), 1),
new HMac(new SHA1Digest()),
new SHA1Digest(),
null);
IESParameters p = new IESParameters(null, null, KEY_SIZE);
ParametersWithIV parametersWithIV = new ParametersWithIV(p, new byte[0]);
iesEngine.setHashMacKey(false);
iesEngine.init(new ECPrivateKeyParameters(privKey, CURVE), parametersWithIV,
new ECIESPublicKeyParser(ECKey.CURVE));
return iesEngine.processBlock(cipher, 0, cipher.length);
}
public static byte[] encrypt(ECPoint toPub, byte[] plaintext) {
return encrypt(toPub, plaintext, null);
}
public static byte[] encrypt(ECPoint toPub, byte[] plaintext, byte[] macData) {
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;
try {
cipher = iesEngine.processBlock(plaintext, 0, plaintext.length, macData);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write(pub.getEncoded(false));
bos.write(IV);
bos.write(cipher);
return bos.toByteArray();
} catch (InvalidCipherTextException e) {
throw Throwables.propagate(e);
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
/**
* Encryption equivalent to the Crypto++ default ECIES<ECP> settings:
*
* DL_KeyAgreementAlgorithm: DL_KeyAgreementAlgorithm_DH<struct ECPPoint,struct EnumToType<enum CofactorMultiplicationOption,0> >
* DL_KeyDerivationAlgorithm: DL_KeyDerivationAlgorithm_P1363<struct ECPPoint,0,class P1363_KDF2<class SHA1> >
* DL_SymmetricEncryptionAlgorithm: DL_EncryptionAlgorithm_Xor<class HMAC<class SHA1>,0>
* DL_PrivateKey: DL_Key<ECPPoint>
* DL_PrivateKey_EC<class ECP>
*
* Used for Whisper V3
*/
public static byte[] encryptSimple(ECPoint pub, byte[] plaintext) throws IOException, InvalidCipherTextException {
EthereumIESEngine iesEngine = new EthereumIESEngine(
new ECDHBasicAgreement(),
new MGF1BytesGeneratorExt(new SHA1Digest(), 1),
new HMac(new SHA1Digest()),
new SHA1Digest(),
null);
IESParameters p = new IESParameters(null, null, KEY_SIZE);
ParametersWithIV parametersWithIV = new ParametersWithIV(p, new byte[0]);
iesEngine.setHashMacKey(false);
ECKeyPairGenerator eGen = new ECKeyPairGenerator();
SecureRandom random = new SecureRandom();
KeyGenerationParameters gParam = new ECKeyGenerationParameters(CURVE, random);
eGen.init(gParam);
// AsymmetricCipherKeyPairGenerator testGen = new AsymmetricCipherKeyPairGenerator() {
// ECKey priv = ECKey.fromPrivate(Hex.decode("d0b043b4c5d657670778242d82d68a29d25d7d711127d17b8e299f156dad361a"));
//
// @Override
// public void init(KeyGenerationParameters keyGenerationParameters) {
// }
//
// @Override
// public AsymmetricCipherKeyPair generateKeyPair() {
// return new AsymmetricCipherKeyPair(new ECPublicKeyParameters(priv.getPubKeyPoint(), CURVE),
// new ECPrivateKeyParameters(priv.getPrivKey(), CURVE));
// }
// };
EphemeralKeyPairGenerator ephemeralKeyPairGenerator =
new EphemeralKeyPairGenerator(/*testGen*/eGen, new ECIESPublicKeyEncoder());
iesEngine.init(new ECPublicKeyParameters(pub, CURVE), parametersWithIV, ephemeralKeyPairGenerator);
return iesEngine.processBlock(plaintext, 0, plaintext.length);
}
private static EthereumIESEngine makeIESEngine(boolean isEncrypt, ECPoint pub, BigInteger prv, byte[] IV) {
AESFastEngine aesFastEngine = new AESFastEngine();
EthereumIESEngine iesEngine = new EthereumIESEngine(
new ECDHBasicAgreement(),
new ConcatKDFBytesGenerator(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, KEY_SIZE, KEY_SIZE);
ParametersWithIV parametersWithIV = new ParametersWithIV(p, IV);
iesEngine.init(isEncrypt, new ECPrivateKeyParameters(prv, CURVE), new ECPublicKeyParameters(pub, CURVE), parametersWithIV);
return iesEngine;
}
public static int getOverhead() {
// 256 bit EC public key, IV, 256 bit MAC
return 65 + KEY_SIZE/8 + 32;
}
}