@@ -45,11 +45,5 @@ | |||||
<scope>compile</scope> | <scope>compile</scope> | ||||
</dependency> | </dependency> | ||||
<dependency> | |||||
<groupId>com.n1analytics</groupId> | |||||
<artifactId>javallier_2.10</artifactId> | |||||
<version>0.6.0</version> | |||||
</dependency> | |||||
</dependencies> | </dependencies> | ||||
</project> | </project> |
@@ -2,17 +2,15 @@ package com.jd.blockchain.crypto.mpc; | |||||
import java.math.BigInteger; | import java.math.BigInteger; | ||||
import com.n1analytics.paillier.PaillierPrivateKey; | |||||
import com.n1analytics.paillier.PaillierPublicKey; | |||||
import com.jd.blockchain.crypto.paillier.PaillierPublicKeyParameters; | |||||
import com.jd.blockchain.crypto.paillier.PaillierUtils; | |||||
import org.bouncycastle.crypto.AsymmetricCipherKeyPair; | import org.bouncycastle.crypto.AsymmetricCipherKeyPair; | ||||
import org.bouncycastle.crypto.CipherParameters; | |||||
import org.bouncycastle.crypto.agreement.ECDHBasicAgreement; | import org.bouncycastle.crypto.agreement.ECDHBasicAgreement; | ||||
import org.bouncycastle.crypto.params.ECDomainParameters; | import org.bouncycastle.crypto.params.ECDomainParameters; | ||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters; | import org.bouncycastle.crypto.params.ECPrivateKeyParameters; | ||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters; | import org.bouncycastle.crypto.params.ECPublicKeyParameters; | ||||
import org.bouncycastle.math.ec.ECCurve; | import org.bouncycastle.math.ec.ECCurve; | ||||
import org.bouncycastle.math.ec.ECPoint; | import org.bouncycastle.math.ec.ECPoint; | ||||
import org.bouncycastle.util.encoders.Hex; | |||||
import com.jd.blockchain.crypto.utils.sm.SM2Utils; | import com.jd.blockchain.crypto.utils.sm.SM2Utils; | ||||
import com.jd.blockchain.crypto.utils.sm.SM3Utils; | import com.jd.blockchain.crypto.utils.sm.SM3Utils; | ||||
@@ -20,68 +18,73 @@ import com.jd.blockchain.utils.io.BytesUtils; | |||||
public class MultiSum { | public class MultiSum { | ||||
private ECPrivateKeyParameters ePrivKey; | |||||
private ECPublicKeyParameters ePubKey; | |||||
private ECCurve curve; | |||||
private ECDomainParameters domainParams; | |||||
private static byte[] ePrivKey; | |||||
private static byte[] ePubKey; | |||||
private static ECCurve curve; | |||||
private static ECDomainParameters domainParams; | |||||
public void generateEphemeralKeyPair(){ | |||||
public static void generateEphemeralKeyPair(){ | |||||
AsymmetricCipherKeyPair eKeyPair = SM2Utils.generateKeyPair(); | AsymmetricCipherKeyPair eKeyPair = SM2Utils.generateKeyPair(); | ||||
this.ePrivKey = (ECPrivateKeyParameters) eKeyPair.getPrivate(); | |||||
this.ePubKey = (ECPublicKeyParameters) eKeyPair.getPublic(); | |||||
this.curve = SM2Utils.getCurve(); | |||||
this.domainParams = SM2Utils.getDomainParams(); | |||||
ECPrivateKeyParameters ecPrivKey = (ECPrivateKeyParameters) eKeyPair.getPrivate(); | |||||
ECPublicKeyParameters ecPubKey= (ECPublicKeyParameters) eKeyPair.getPublic(); | |||||
ePrivKey = bigIntegerToBytes(ecPrivKey.getD()); | |||||
ePubKey = ecPubKey.getQ().getEncoded(false); | |||||
curve = SM2Utils.getCurve(); | |||||
domainParams = SM2Utils.getDomainParams(); | |||||
} | } | ||||
public BigInteger calculateAgreement(CipherParameters otherEPubKey){ | |||||
public static byte[] calculateAgreement(byte[] otherEPubKey, byte[] ePrivKey){ | |||||
ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement(); | ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement(); | ||||
basicAgreement.init(ePrivKey); | |||||
return basicAgreement.calculateAgreement(otherEPubKey); | |||||
ECPoint ePubKeyPoint = resolvePubKeyBytes(otherEPubKey); | |||||
ECPublicKeyParameters pubKey = new ECPublicKeyParameters(ePubKeyPoint, domainParams); | |||||
ECPrivateKeyParameters privateKey = new ECPrivateKeyParameters(new BigInteger(1,ePrivKey), domainParams); | |||||
basicAgreement.init(privateKey); | |||||
BigInteger agreement = basicAgreement.calculateAgreement(pubKey); | |||||
return bigIntegerToBytes(agreement); | |||||
} | } | ||||
public static BigInteger deriveShares(byte[] frontID, byte[] rearID, BigInteger agreement){ | |||||
byte[] agreementBytes = agreement.toByteArray(); | |||||
public static byte[] deriveShares(byte[] frontID, byte[] rearID, byte[] agreementBytes){ | |||||
byte[] inputBytes = BytesUtils.concat(frontID,rearID,agreementBytes); | byte[] inputBytes = BytesUtils.concat(frontID,rearID,agreementBytes); | ||||
return new BigInteger(1,SM3Utils.hash(inputBytes)); | |||||
return SM3Utils.hash(inputBytes); | |||||
} | } | ||||
public static BigInteger encryptBlindedMsg(PaillierPublicKey encKey, BigInteger msg, BigInteger frontShare, BigInteger rearShare){ | |||||
public static byte[] encryptBlindedMsg(byte[] paillierPubKey, int input, byte[] frontShare, byte[] rearShare){ | |||||
BigInteger integer = BigInteger.valueOf(input); | |||||
BigInteger frontInteger = new BigInteger(1,frontShare); | |||||
BigInteger rearInteger = new BigInteger(1,rearShare); | |||||
PaillierPublicKeyParameters encKey = PaillierUtils.bytes2PubKey(paillierPubKey); | |||||
BigInteger modulus = encKey.getModulus(); | BigInteger modulus = encKey.getModulus(); | ||||
BigInteger plaintext = msg.add(frontShare).subtract(rearShare).mod(modulus); | |||||
return encKey.raw_encrypt(plaintext); | |||||
BigInteger plaintext = integer.add(frontInteger).subtract(rearInteger).mod(modulus); | |||||
return PaillierUtils.encrypt(plaintext.toByteArray(),encKey); | |||||
} | } | ||||
public static BigInteger aggregateCiphertexts(PaillierPublicKey encKey, BigInteger... bigIntegers){ | |||||
BigInteger aggregatedCiphertext = BigInteger.ONE; | |||||
BigInteger modulusSquared = encKey.getModulusSquared(); | |||||
for (BigInteger entry : bigIntegers) { | |||||
aggregatedCiphertext = aggregatedCiphertext.multiply(entry).mod(modulusSquared); | |||||
} | |||||
return aggregatedCiphertext; | |||||
public static byte[] aggregateCiphertexts(byte[] paillierPubKey, byte[]... ciphertexts){ | |||||
return PaillierUtils.add(paillierPubKey,ciphertexts); | |||||
} | } | ||||
public static BigInteger decrypt(PaillierPrivateKey decKey, BigInteger ciphertext){ | |||||
return decKey.raw_decrypt(ciphertext); | |||||
public static byte[] decrypt(byte[] paillierPrivKey, byte[] ciphertext){ | |||||
return PaillierUtils.decrypt(ciphertext,paillierPrivKey); | |||||
} | } | ||||
public ECPublicKeyParameters getEPubKey(){return ePubKey;} | |||||
public static byte[] getEPubKey(){return ePubKey;} | |||||
public ECPrivateKeyParameters getEPrivKey(){return ePrivKey;} | |||||
public static byte[] getEPrivKey(){return ePrivKey;} | |||||
public byte[] getEPubKeyBytes(){ | |||||
byte[] ePubKeyBytes = new byte[65]; | |||||
byte[] ePubKeyBytesX = ePubKey.getQ().getAffineXCoord().getEncoded(); | |||||
byte[] ePubKeyBytesY = ePubKey.getQ().getAffineYCoord().getEncoded(); | |||||
System.arraycopy(Hex.decode("04"),0,ePubKeyBytes,0,1); | |||||
System.arraycopy(ePubKeyBytesX,0,ePubKeyBytes,1,32); | |||||
System.arraycopy(ePubKeyBytesY,0,ePubKeyBytes,1+32,32); | |||||
return ePubKeyBytes; | |||||
} | |||||
public byte[] getEPrivKeyBytes(){ | |||||
return BigIntegerToLBytes(ePrivKey.getD(),32); | |||||
} | |||||
// public byte[] getEPubKeyBytes(){ | |||||
// byte[] ePubKeyBytes = new byte[65]; | |||||
// byte[] ePubKeyBytesX = ePubKey.getQ().getAffineXCoord().getEncoded(); | |||||
// byte[] ePubKeyBytesY = ePubKey.getQ().getAffineYCoord().getEncoded(); | |||||
// System.arraycopy(Hex.decode("04"),0,ePubKeyBytes,0,1); | |||||
// System.arraycopy(ePubKeyBytesX,0,ePubKeyBytes,1,32); | |||||
// System.arraycopy(ePubKeyBytesY,0,ePubKeyBytes,1+32,32); | |||||
// return ePubKeyBytes; | |||||
// } | |||||
// | |||||
// public byte[] getEPrivKeyBytes(){ | |||||
// return bigIntegerToBytes(ePrivKey.getD()); | |||||
// } | |||||
public ECPublicKeyParameters resolveEPubKey(byte[] ePubKeyBytes){ | public ECPublicKeyParameters resolveEPubKey(byte[] ePubKeyBytes){ | ||||
byte[] ePubKeyX = new byte[32]; | byte[] ePubKeyX = new byte[32]; | ||||
@@ -97,9 +100,9 @@ public class MultiSum { | |||||
} | } | ||||
// To convert BigInteger to byte[] whose length is l | // To convert BigInteger to byte[] whose length is l | ||||
private static byte[] BigIntegerToLBytes(BigInteger b, int l){ | |||||
private static byte[] bigIntegerToBytes(BigInteger b){ | |||||
byte[] tmp = b.toByteArray(); | byte[] tmp = b.toByteArray(); | ||||
byte[] result = new byte[l]; | |||||
byte[] result = new byte[32]; | |||||
if (tmp.length > result.length) { | if (tmp.length > result.length) { | ||||
System.arraycopy(tmp, tmp.length - result.length, result, 0, result.length); | System.arraycopy(tmp, tmp.length - result.length, result, 0, result.length); | ||||
} | } | ||||
@@ -108,4 +111,10 @@ public class MultiSum { | |||||
} | } | ||||
return result; | return result; | ||||
} | } | ||||
// To retrieve the public key point from publicKey in byte array mode | |||||
private static ECPoint resolvePubKeyBytes(byte[] publicKey){ | |||||
return curve.decodePoint(publicKey); | |||||
} | |||||
} | } |
@@ -0,0 +1,51 @@ | |||||
package com.jd.blockchain.crypto.paillier; | |||||
import org.bouncycastle.crypto.AsymmetricCipherKeyPair; | |||||
import org.bouncycastle.math.Primes; | |||||
import org.bouncycastle.util.BigIntegers; | |||||
import java.math.BigInteger; | |||||
import java.security.SecureRandom; | |||||
/** | |||||
* @author zhanglin33 | |||||
* @title: PaillierKeyPairGenerator | |||||
* @description: generator of paillier key pair | |||||
* @date 2019-04-30, 14:48 | |||||
*/ | |||||
public class PaillierKeyPairGenerator { | |||||
private static final int STRENGTH = 2048; | |||||
public AsymmetricCipherKeyPair generateKeyPair() { | |||||
int pLength = (STRENGTH + 1) / 2; | |||||
int qLength = STRENGTH - pLength; | |||||
BigInteger p; | |||||
BigInteger q; | |||||
BigInteger n; | |||||
do { | |||||
do { | |||||
SecureRandom pRandom = new SecureRandom(); | |||||
p = BigIntegers.createRandomPrime(pLength, 1, pRandom); | |||||
} while (!isProbablePrime(p)); | |||||
do { | |||||
SecureRandom qRandom = new SecureRandom(); | |||||
q = BigIntegers.createRandomPrime(qLength, 1, qRandom); | |||||
} while (q.equals(p) || !isProbablePrime(p)); | |||||
n = q.multiply(p); | |||||
} while (n.bitLength() != STRENGTH); | |||||
return new AsymmetricCipherKeyPair(new PaillierPublicKeyParameters(n), new PaillierPrivateKeyParameters(p,q)); | |||||
} | |||||
// Primes class for FIPS 186-4 C.3 primality checking | |||||
private boolean isProbablePrime(BigInteger x) | |||||
{ | |||||
int iterations = 3; | |||||
SecureRandom random = new SecureRandom(); | |||||
return !Primes.hasAnySmallFactors(x) && Primes.isMRProbablePrime(x, random, iterations); | |||||
} | |||||
} |
@@ -0,0 +1,101 @@ | |||||
package com.jd.blockchain.crypto.paillier; | |||||
import org.bouncycastle.crypto.params.AsymmetricKeyParameter; | |||||
import java.math.BigInteger; | |||||
import static org.bouncycastle.util.BigIntegers.ONE; | |||||
/** | |||||
* @author zhanglin33 | |||||
* @title: PaillierPrivateKeyParameters | |||||
* @description: parameters about Paillier private key | |||||
* @date 2019-04-30, 14:39 | |||||
*/ | |||||
public class PaillierPrivateKeyParameters extends AsymmetricKeyParameter { | |||||
private BigInteger p; | |||||
private BigInteger q; | |||||
private BigInteger pSquared; | |||||
private BigInteger qSquared; | |||||
private BigInteger pInverse; | |||||
private BigInteger muP; | |||||
private BigInteger muQ; | |||||
public PaillierPrivateKeyParameters(BigInteger p, BigInteger q) { | |||||
super(true); | |||||
BigInteger generator = p.multiply(q).add(ONE); | |||||
this.p = p; | |||||
this.pSquared = p.multiply(p); | |||||
this.q = q; | |||||
this.qSquared = q.multiply(q); | |||||
this.pInverse = p.modInverse(q); | |||||
this.muP = hFunction(generator, p, pSquared); | |||||
this.muQ = hFunction(generator, q, qSquared); | |||||
} | |||||
public PaillierPrivateKeyParameters(BigInteger p, BigInteger pSquared, BigInteger q, BigInteger qSquared, | |||||
BigInteger pInverse, BigInteger muP, BigInteger muQ){ | |||||
super(true); | |||||
this.p = p; | |||||
this.pSquared = pSquared; | |||||
this.q = q; | |||||
this.qSquared = qSquared; | |||||
this.pInverse = pInverse; | |||||
this.muP = muP; | |||||
this.muQ = muQ; | |||||
} | |||||
// mu = h(x) = (L(g^(x-1) mod x^2))^(-1) mod n | |||||
private BigInteger hFunction(BigInteger generator, BigInteger x, BigInteger xSquared) { | |||||
BigInteger phiX = lFunction(generator.modPow(x.subtract(ONE),xSquared),x); | |||||
return phiX.modInverse(x); | |||||
} | |||||
// L(x) = (x-1) / n | |||||
public BigInteger lFunction(BigInteger x, BigInteger n) { | |||||
return x.subtract(ONE).divide(n); | |||||
} | |||||
public BigInteger getP() | |||||
{ | |||||
return p; | |||||
} | |||||
public BigInteger getPSquared() | |||||
{ | |||||
return pSquared; | |||||
} | |||||
public BigInteger getQ() | |||||
{ | |||||
return q; | |||||
} | |||||
public BigInteger getQSquared() | |||||
{ | |||||
return qSquared; | |||||
} | |||||
public BigInteger getPInverse() | |||||
{ | |||||
return pInverse; | |||||
} | |||||
public BigInteger getMuP() | |||||
{ | |||||
return muP; | |||||
} | |||||
public BigInteger getMuQ() | |||||
{ | |||||
return muQ; | |||||
} | |||||
} |
@@ -0,0 +1,59 @@ | |||||
package com.jd.blockchain.crypto.paillier; | |||||
import org.bouncycastle.crypto.params.AsymmetricKeyParameter; | |||||
import java.math.BigInteger; | |||||
import static org.bouncycastle.util.BigIntegers.ONE; | |||||
/** | |||||
* @author zhanglin33 | |||||
* @title: PaillierPublicKeyParameters | |||||
* @description: parameters about Paillier public key | |||||
* @date 2019-04-30, 14:41 | |||||
*/ | |||||
public class PaillierPublicKeyParameters extends AsymmetricKeyParameter { | |||||
private BigInteger modulus; | |||||
private BigInteger modulusSquared; | |||||
private BigInteger generator; | |||||
public PaillierPublicKeyParameters(BigInteger modulus) { | |||||
super(false); | |||||
this.modulus = validate(modulus); | |||||
this.modulusSquared = modulus.multiply(modulus); | |||||
this.generator = modulus.add(ONE); | |||||
} | |||||
public BigInteger getModulus() { | |||||
return modulus; | |||||
} | |||||
public BigInteger getModulusSquared() { | |||||
return modulusSquared; | |||||
} | |||||
public BigInteger getGenerator() { | |||||
return generator; | |||||
} | |||||
private BigInteger validate(BigInteger modulus) | |||||
{ | |||||
if ((modulus.intValue() & 1) == 0) | |||||
{ | |||||
throw new IllegalArgumentException("The modulus is even!"); | |||||
} | |||||
// the value is the product of the 132 smallest primes from 3 to 751 | |||||
if (!modulus.gcd(new BigInteger("145188775577763990151158743208307020242261438098488931355057091965" + | |||||
"931517706595657435907891265414916764399268423699130577757433083166" + | |||||
"651158914570105971074227669275788291575622090199821297575654322355" + | |||||
"049043101306108213104080801056529374892690144291505781966373045481" + | |||||
"8359472391642885328171302299245556663073719855")).equals(ONE)) | |||||
{ | |||||
throw new IllegalArgumentException("The modulus has a small prime factor!"); | |||||
} | |||||
return modulus; | |||||
} | |||||
} |
@@ -0,0 +1,218 @@ | |||||
package com.jd.blockchain.crypto.paillier; | |||||
import com.jd.blockchain.utils.io.BytesUtils; | |||||
import org.bouncycastle.crypto.AsymmetricCipherKeyPair; | |||||
import java.math.BigInteger; | |||||
import java.security.SecureRandom; | |||||
import static org.bouncycastle.util.BigIntegers.ONE; | |||||
/** | |||||
* @author zhanglin33 | |||||
* @title: PaillierUtils | |||||
* @description: encryption, decryption, homomorphic addition and scalar multiplication in Paillier algorithm | |||||
* @date 2019-04-30, 14:49 | |||||
*/ | |||||
public class PaillierUtils { | |||||
private static final int MODULUS_LENGTH = 256; | |||||
private static final int MODULUSSQUARED_LENGTH = 512; | |||||
private static final int P_LENGTH = 128; | |||||
private static final int PSQUARED_LENGTH = 256; | |||||
private static final int Q_LENGTH = 128; | |||||
private static final int QSQUARED_LENGTH = 256; | |||||
private static final int PINVERSE_LENGTH = 128; | |||||
private static final int MUP_LENGTH = 128; | |||||
private static final int MUQ_LENGTH = 128; | |||||
private static final int PRIVKEY_LENGTH = P_LENGTH + PSQUARED_LENGTH + Q_LENGTH + QSQUARED_LENGTH | |||||
+ PINVERSE_LENGTH + MUP_LENGTH + MUQ_LENGTH; | |||||
public static AsymmetricCipherKeyPair generateKeyPair(){ | |||||
PaillierKeyPairGenerator generator = new PaillierKeyPairGenerator(); | |||||
return generator.generateKeyPair(); | |||||
} | |||||
public static byte[] encrypt(byte[] plainBytes, byte[] publicKey) { | |||||
PaillierPublicKeyParameters pubKeyParams = bytes2PubKey(publicKey); | |||||
return encrypt(plainBytes, pubKeyParams); | |||||
} | |||||
public static byte[] encrypt(byte[] plainBytes, PaillierPublicKeyParameters pubKeyParams) { | |||||
SecureRandom random = new SecureRandom(); | |||||
return encrypt(plainBytes, pubKeyParams, random); | |||||
} | |||||
// c = g^m * r^n mod n^2 = (1+n)^m * r^n mod n^2 = (1 + n*m mod n^2) * r^n mod n^2 | |||||
public static byte[] encrypt(byte[] plainBytes, PaillierPublicKeyParameters pubKeyParams, SecureRandom random){ | |||||
BigInteger n = pubKeyParams.getModulus(); | |||||
BigInteger nSquared = pubKeyParams.getModulusSquared(); | |||||
BigInteger m = new BigInteger(1,plainBytes); | |||||
BigInteger r = new BigInteger(n.bitLength(), random); | |||||
BigInteger rawCiphertext = n.multiply(m).add(ONE).mod(nSquared); | |||||
BigInteger c = r.modPow(n, nSquared).multiply(rawCiphertext).mod(nSquared); | |||||
return bigIntegerToBytes(c, MODULUSSQUARED_LENGTH); | |||||
} | |||||
public static byte[] decrypt(byte[] cipherBytes, byte[] privateKey) { | |||||
PaillierPrivateKeyParameters privKeyParams = bytes2PrivKey(privateKey); | |||||
return decrypt(cipherBytes,privKeyParams); | |||||
} | |||||
// m mod p = L(c^(p-1) mod p^2) * muP mod p | |||||
// m mod q = L(c^(q-1) mod q^2) * muQ mod q | |||||
// m = (m mod p) * (1/q mod p) * q + (m mod q) * (1/p mod q) * p | |||||
// = ((m mod q)-(m mod p)) * (1/p mod q) mod q * p + (m mod p) | |||||
public static byte[] decrypt(byte[] cipherBytes, PaillierPrivateKeyParameters privKeyParams){ | |||||
BigInteger cihphertext = new BigInteger(1, cipherBytes); | |||||
BigInteger p = privKeyParams.getP(); | |||||
BigInteger pSquared = privKeyParams.getPSquared(); | |||||
BigInteger q = privKeyParams.getQ(); | |||||
BigInteger qSquared = privKeyParams.getQSquared(); | |||||
BigInteger pInverse = privKeyParams.getPInverse(); | |||||
BigInteger muP = privKeyParams.getMuP(); | |||||
BigInteger muQ = privKeyParams.getMuQ(); | |||||
BigInteger mModP = | |||||
privKeyParams.lFunction(cihphertext.modPow(p.subtract(ONE),pSquared),p).multiply(muP).mod(p); | |||||
BigInteger mModQ = | |||||
privKeyParams.lFunction(cihphertext.modPow(q.subtract(ONE),qSquared),q).multiply(muQ).mod(q); | |||||
BigInteger midValue = mModQ.subtract(mModP).multiply(pInverse).mod(q); | |||||
BigInteger m = midValue.multiply(p).add(mModP); | |||||
return m.toByteArray(); | |||||
} | |||||
public static byte[] pubKey2Bytes(PaillierPublicKeyParameters pubKeyParams) { | |||||
BigInteger n = pubKeyParams.getModulus(); | |||||
return bigIntegerToBytes(n, MODULUS_LENGTH); | |||||
} | |||||
public static PaillierPublicKeyParameters bytes2PubKey(byte[] publicKey) { | |||||
if (publicKey.length != MODULUS_LENGTH) { | |||||
throw new IllegalArgumentException("publicKey's length does not meet algorithm's requirement!"); | |||||
} | |||||
BigInteger n = new BigInteger(1, publicKey); | |||||
return new PaillierPublicKeyParameters(n); | |||||
} | |||||
public static byte[] privKey2Bytes(PaillierPrivateKeyParameters privKeyParams) { | |||||
BigInteger p = privKeyParams.getP(); | |||||
BigInteger pSquared = privKeyParams.getPSquared(); | |||||
BigInteger q = privKeyParams.getQ(); | |||||
BigInteger qSquared = privKeyParams.getQSquared(); | |||||
BigInteger pInverse = privKeyParams.getPInverse(); | |||||
BigInteger muP = privKeyParams.getMuP(); | |||||
BigInteger muQ = privKeyParams.getMuQ(); | |||||
byte[] pBytes = bigIntegerToBytes(p, P_LENGTH); | |||||
byte[] pSquaredBytes = bigIntegerToBytes(pSquared, PSQUARED_LENGTH); | |||||
byte[] qBytes = bigIntegerToBytes(q, Q_LENGTH); | |||||
byte[] qSquaredBytes = bigIntegerToBytes(qSquared, QSQUARED_LENGTH); | |||||
byte[] pInverseBytes = bigIntegerToBytes(pInverse, PINVERSE_LENGTH); | |||||
byte[] muPBytes = bigIntegerToBytes(muP, MUP_LENGTH); | |||||
byte[] muQBytes = bigIntegerToBytes(muQ, MUQ_LENGTH); | |||||
return BytesUtils.concat(pBytes,pSquaredBytes,qBytes,qSquaredBytes,pInverseBytes,muPBytes,muQBytes); | |||||
} | |||||
public static PaillierPrivateKeyParameters bytes2PrivKey(byte[] privateKey) { | |||||
if (privateKey.length != PRIVKEY_LENGTH) { | |||||
throw new IllegalArgumentException("privateKey's length does not meet algorithm's requirement!"); | |||||
} | |||||
byte[] pBytes = new byte[P_LENGTH]; | |||||
byte[] pSquaredBytes = new byte[PSQUARED_LENGTH]; | |||||
byte[] qBytes = new byte[Q_LENGTH]; | |||||
byte[] qSquaredBytes = new byte[QSQUARED_LENGTH]; | |||||
byte[] pInverseBytes = new byte[PINVERSE_LENGTH]; | |||||
byte[] muPBytes = new byte[MUP_LENGTH]; | |||||
byte[] muQBytes = new byte[MUQ_LENGTH]; | |||||
split(privateKey,pBytes,pSquaredBytes,qBytes,qSquaredBytes,pInverseBytes,muPBytes,muQBytes); | |||||
BigInteger p = new BigInteger(1, pBytes); | |||||
BigInteger pSquared = new BigInteger(1, pSquaredBytes); | |||||
BigInteger q = new BigInteger(1, qBytes); | |||||
BigInteger qSquared = new BigInteger(1, qSquaredBytes); | |||||
BigInteger pInverse = new BigInteger(1, pInverseBytes); | |||||
BigInteger muP = new BigInteger(1, muPBytes); | |||||
BigInteger muQ = new BigInteger(1, muQBytes); | |||||
return new PaillierPrivateKeyParameters(p,pSquared,q,qSquared,pInverse,muP,muQ); | |||||
} | |||||
public static byte[] add(byte[] publicKey, byte[]... ciphertexts) { | |||||
PaillierPublicKeyParameters pubKeyParams = bytes2PubKey(publicKey); | |||||
return add(pubKeyParams,ciphertexts); | |||||
} | |||||
public static byte[] add(PaillierPublicKeyParameters pubKeyParams, byte[]... ciphertexts) { | |||||
BigInteger result = ONE; | |||||
BigInteger multiplier; | |||||
BigInteger nSquared = pubKeyParams.getModulusSquared(); | |||||
for (byte[] each: ciphertexts) { | |||||
multiplier = new BigInteger(1, each); | |||||
result = result.multiply(multiplier).mod(nSquared); | |||||
} | |||||
return bigIntegerToBytes(result, MODULUSSQUARED_LENGTH); | |||||
} | |||||
public static byte[] scalarMultiply(byte[] publicKey, byte[] cipherBytes, long scalar) { | |||||
PaillierPublicKeyParameters pubKeyParams = bytes2PubKey(publicKey); | |||||
return scalarMultiply(pubKeyParams,cipherBytes,scalar); | |||||
} | |||||
public static byte[] scalarMultiply(PaillierPublicKeyParameters pubKeyParams, byte[] cipherBytes, long scalar) { | |||||
BigInteger nSquared = pubKeyParams.getModulusSquared(); | |||||
BigInteger cihertext = new BigInteger(1, cipherBytes); | |||||
BigInteger exponent = BigInteger.valueOf(scalar); | |||||
BigInteger result = cihertext.modPow(exponent,nSquared); | |||||
return bigIntegerToBytes(result, MODULUSSQUARED_LENGTH); | |||||
} | |||||
// To convert BigInteger to byte array in specified size | |||||
private static byte[] bigIntegerToBytes(BigInteger b, int bytesSize){ | |||||
byte[] tmp = b.toByteArray(); | |||||
byte[] result = new byte[bytesSize]; | |||||
if (tmp.length > result.length) { | |||||
System.arraycopy(tmp, tmp.length - result.length, result, 0, result.length); | |||||
} | |||||
else { | |||||
System.arraycopy(tmp,0,result,result.length-tmp.length,tmp.length); | |||||
} | |||||
return result; | |||||
} | |||||
private static void split(byte[] src, byte[]... bytesList) { | |||||
int srcPos = 0; | |||||
for (byte[] each: bytesList){ | |||||
System.arraycopy(src,srcPos,each,0,each.length); | |||||
srcPos += each.length; | |||||
if (srcPos >= src.length){ | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,103 +1,88 @@ | |||||
package test.com.jd.blockchain.crypto.mpc; | package test.com.jd.blockchain.crypto.mpc; | ||||
import com.jd.blockchain.crypto.mpc.MultiSum; | import com.jd.blockchain.crypto.mpc.MultiSum; | ||||
import com.n1analytics.paillier.PaillierPrivateKey; | |||||
import com.n1analytics.paillier.PaillierPublicKey; | |||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters; | |||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters; | |||||
import org.bouncycastle.util.encoders.Hex; | |||||
import org.junit.Before; | |||||
import com.jd.blockchain.crypto.paillier.PaillierPrivateKeyParameters; | |||||
import com.jd.blockchain.crypto.paillier.PaillierPublicKeyParameters; | |||||
import com.jd.blockchain.crypto.paillier.PaillierUtils; | |||||
import org.bouncycastle.crypto.AsymmetricCipherKeyPair; | |||||
import org.junit.Test; | import org.junit.Test; | ||||
import java.math.BigInteger; | |||||
import static org.junit.Assert.*; | import static org.junit.Assert.*; | ||||
public class MultiSumTest { | public class MultiSumTest { | ||||
private PaillierPrivateKey decKey; | |||||
private PaillierPublicKey encKey; | |||||
@Before | |||||
public void init() { | |||||
decKey = PaillierPrivateKey.create(2048); | |||||
encKey = decKey.getPublicKey(); | |||||
} | |||||
@Test | @Test | ||||
public void testMultiSum() { | public void testMultiSum() { | ||||
MultiSum instance1 = new MultiSum(); | |||||
MultiSum instance2 = new MultiSum(); | |||||
MultiSum instance3 = new MultiSum(); | |||||
AsymmetricCipherKeyPair keyPair = PaillierUtils.generateKeyPair(); | |||||
PaillierPublicKeyParameters pubKeyParams = (PaillierPublicKeyParameters) keyPair.getPublic(); | |||||
PaillierPrivateKeyParameters privKeyParams = (PaillierPrivateKeyParameters) keyPair.getPrivate(); | |||||
BigInteger value1 = BigInteger.valueOf(6); | |||||
BigInteger value2 = BigInteger.valueOf(60); | |||||
BigInteger value3 = BigInteger.valueOf(600); | |||||
BigInteger expectedSum = BigInteger.valueOf(666); | |||||
byte[] encKey = PaillierUtils.pubKey2Bytes(pubKeyParams); | |||||
byte[] decKey = PaillierUtils.privKey2Bytes(privKeyParams); | |||||
int int1 = 6; | |||||
int int2 = 60; | |||||
int int3 = 600; | |||||
int sum = 666; | |||||
byte[] id1 = "1".getBytes(); | byte[] id1 = "1".getBytes(); | ||||
byte[] id2 = "2".getBytes(); | byte[] id2 = "2".getBytes(); | ||||
byte[] id3 = "3".getBytes(); | byte[] id3 = "3".getBytes(); | ||||
instance1.generateEphemeralKeyPair(); | |||||
instance2.generateEphemeralKeyPair(); | |||||
instance3.generateEphemeralKeyPair(); | |||||
MultiSum.generateEphemeralKeyPair(); | |||||
byte[] ePubKey1 = MultiSum.getEPubKey(); | |||||
byte[] ePrivKey1 = MultiSum.getEPrivKey(); | |||||
ECPublicKeyParameters ePubKey1 = instance1.getEPubKey(); | |||||
ECPublicKeyParameters ePubKey2 = instance2.getEPubKey(); | |||||
ECPublicKeyParameters ePubKey3 = instance3.getEPubKey(); | |||||
MultiSum.generateEphemeralKeyPair(); | |||||
byte[] ePubKey2 = MultiSum.getEPubKey(); | |||||
byte[] ePrivKey2 = MultiSum.getEPrivKey(); | |||||
BigInteger sk12 = instance1.calculateAgreement(ePubKey2); | |||||
BigInteger sk23 = instance2.calculateAgreement(ePubKey3); | |||||
BigInteger sk31 = instance1.calculateAgreement(ePubKey3); | |||||
MultiSum.generateEphemeralKeyPair(); | |||||
byte[] ePubKey3 = MultiSum.getEPubKey(); | |||||
byte[] ePrivKey3 = MultiSum.getEPrivKey(); | |||||
assertEquals(sk12,instance2.calculateAgreement(ePubKey1)); | |||||
assertEquals(sk23,instance3.calculateAgreement(ePubKey2)); | |||||
assertEquals(sk31,instance3.calculateAgreement(ePubKey1)); | |||||
BigInteger s12 = MultiSum.deriveShares(id1,id2,sk12); | |||||
BigInteger s23 = MultiSum.deriveShares(id2,id3,sk23); | |||||
BigInteger s31 = MultiSum.deriveShares(id3,id1,sk31); | |||||
byte[] sk12 = MultiSum.calculateAgreement(ePubKey2,ePrivKey1); | |||||
byte[] sk23 = MultiSum.calculateAgreement(ePubKey3,ePrivKey2); | |||||
byte[] sk31 = MultiSum.calculateAgreement(ePubKey1,ePrivKey3); | |||||
assertEquals(s12, MultiSum.deriveShares(id1,id2,sk12)); | |||||
assertEquals(s23, MultiSum.deriveShares(id2,id3,sk23)); | |||||
assertEquals(s31, MultiSum.deriveShares(id3,id1,sk31)); | |||||
assertArrayEquals(sk12,MultiSum.calculateAgreement(ePubKey1,ePrivKey2)); | |||||
assertArrayEquals(sk23,MultiSum.calculateAgreement(ePubKey2,ePrivKey3)); | |||||
assertArrayEquals(sk31,MultiSum.calculateAgreement(ePubKey3,ePrivKey1)); | |||||
BigInteger c1 = MultiSum.encryptBlindedMsg(encKey,value1,s12,s31); | |||||
BigInteger c2 = MultiSum.encryptBlindedMsg(encKey,value2,s23,s12); | |||||
BigInteger c3 = MultiSum.encryptBlindedMsg(encKey,value3,s31,s23); | |||||
byte[] s12 = MultiSum.deriveShares(id1,id2,sk12); | |||||
byte[] s23 = MultiSum.deriveShares(id2,id3,sk23); | |||||
byte[] s31 = MultiSum.deriveShares(id3,id1,sk31); | |||||
BigInteger aggregatedCiphertext = MultiSum.aggregateCiphertexts(encKey,c1,c2,c3); | |||||
assertArrayEquals(s12, MultiSum.deriveShares(id1,id2,sk12)); | |||||
assertArrayEquals(s23, MultiSum.deriveShares(id2,id3,sk23)); | |||||
assertArrayEquals(s31, MultiSum.deriveShares(id3,id1,sk31)); | |||||
BigInteger decryptedValue = MultiSum.decrypt(decKey,aggregatedCiphertext); | |||||
byte[] c1 = MultiSum.encryptBlindedMsg(encKey,int1,s12,s31); | |||||
byte[] c2 = MultiSum.encryptBlindedMsg(encKey,int2,s23,s12); | |||||
byte[] c3 = MultiSum.encryptBlindedMsg(encKey,int3,s31,s23); | |||||
assertEquals(expectedSum,decryptedValue); | |||||
} | |||||
byte[] aggregatedCiphertext = MultiSum.aggregateCiphertexts(encKey,c1,c2,c3); | |||||
@Test | |||||
public void testResolveEPrivKey(){ | |||||
byte[] decryptedValue = MultiSum.decrypt(decKey,aggregatedCiphertext); | |||||
MultiSum instance = new MultiSum(); | |||||
instance.generateEphemeralKeyPair(); | |||||
ECPrivateKeyParameters expectedEPrivKey = instance.getEPrivKey(); | |||||
byte[] ePrivKeyBytes = instance.getEPrivKeyBytes(); | |||||
ECPrivateKeyParameters ePrivKey = instance.resolveEPrivKey(ePrivKeyBytes); | |||||
assertEquals(expectedEPrivKey.getD(),ePrivKey.getD()); | |||||
assertEquals(sum,byteArrayToInt(decryptedValue)); | |||||
} | } | ||||
@Test | |||||
public void testResolveEPubKey(){ | |||||
MultiSum instance = new MultiSum(); | |||||
instance.generateEphemeralKeyPair(); | |||||
ECPublicKeyParameters expectedEPubKey = instance.getEPubKey(); | |||||
byte[] ePubKeyBytes = instance.getEPubKeyBytes(); | |||||
ECPublicKeyParameters ePubKey = instance.resolveEPubKey(ePubKeyBytes); | |||||
assertEquals(Hex.toHexString(expectedEPubKey.getQ().getAffineXCoord().getEncoded()),Hex.toHexString(ePubKey.getQ().getAffineXCoord().getEncoded())); | |||||
assertEquals(Hex.toHexString(expectedEPubKey.getQ().getAffineYCoord().getEncoded()),Hex.toHexString(ePubKey.getQ().getAffineYCoord().getEncoded())); | |||||
private static int byteArrayToInt(byte[] input) { | |||||
int result; | |||||
int length = input.length; | |||||
byte[] buffer = new byte[4]; | |||||
if (length <= buffer.length){ | |||||
System.arraycopy(input,0,buffer,buffer.length - length,length); | |||||
} else { | |||||
System.arraycopy(input,length - buffer.length,buffer,0, buffer.length); | |||||
} | |||||
result = buffer[3] & 0xFF | | |||||
(buffer[2] & 0xFF) << 8 | | |||||
(buffer[1] & 0xFF) << 16 | | |||||
(buffer[0] & 0xFF) << 24; | |||||
return result; | |||||
} | } | ||||
} | } |
@@ -0,0 +1,202 @@ | |||||
package test.com.jd.blockchain.crypto.paillier; | |||||
import com.jd.blockchain.crypto.paillier.PaillierPrivateKeyParameters; | |||||
import com.jd.blockchain.crypto.paillier.PaillierPublicKeyParameters; | |||||
import com.jd.blockchain.crypto.paillier.PaillierUtils; | |||||
import org.bouncycastle.crypto.AsymmetricCipherKeyPair; | |||||
import org.junit.Test; | |||||
import java.math.BigInteger; | |||||
import java.security.SecureRandom; | |||||
import static org.junit.Assert.assertEquals; | |||||
/** | |||||
* @author zhanglin33 | |||||
* @title: PaillierUtilsTest | |||||
* @description: Tests on PaillierUtils | |||||
* @date 2019-04-30, 14:54 | |||||
*/ | |||||
public class PaillierUtilsTest { | |||||
@Test | |||||
public void generateKeyPairTest() { | |||||
AsymmetricCipherKeyPair keyPair = PaillierUtils.generateKeyPair(); | |||||
PaillierPublicKeyParameters pubKeyParams = (PaillierPublicKeyParameters) keyPair.getPublic(); | |||||
PaillierPrivateKeyParameters privKeyParams = (PaillierPrivateKeyParameters) keyPair.getPrivate(); | |||||
BigInteger n = pubKeyParams.getModulus(); | |||||
BigInteger nSquared = pubKeyParams.getModulusSquared(); | |||||
BigInteger g = pubKeyParams.getGenerator(); | |||||
BigInteger nConverted = new BigInteger(1, bigIntegerToBytes(n,256)); | |||||
BigInteger nSquaredConverted = new BigInteger(1, bigIntegerToBytes(nSquared,512)); | |||||
BigInteger gConverted = new BigInteger(1, bigIntegerToBytes(g,256)); | |||||
assertEquals(nConverted, n); | |||||
assertEquals(nSquaredConverted, nSquared); | |||||
assertEquals(gConverted, g); | |||||
BigInteger p = privKeyParams.getP(); | |||||
BigInteger pSquared = privKeyParams.getPSquared(); | |||||
BigInteger q = privKeyParams.getQ(); | |||||
BigInteger qSquared = privKeyParams.getQSquared(); | |||||
BigInteger pInverse = privKeyParams.getPInverse(); | |||||
BigInteger muP = privKeyParams.getMuP(); | |||||
BigInteger muQ = privKeyParams.getMuQ(); | |||||
BigInteger pConverted = new BigInteger(1, bigIntegerToBytes(p,128)); | |||||
BigInteger pSquaredConverted = new BigInteger(1, bigIntegerToBytes(pSquared,256)); | |||||
BigInteger qConverted = new BigInteger(1, bigIntegerToBytes(q,128)); | |||||
BigInteger qSquaredConverted = new BigInteger(1, bigIntegerToBytes(qSquared,256)); | |||||
BigInteger pInverseConverted = new BigInteger(1, bigIntegerToBytes(pInverse,128)); | |||||
BigInteger muPConverted = new BigInteger(1, bigIntegerToBytes(muP,128)); | |||||
BigInteger muQConverted = new BigInteger(1, bigIntegerToBytes(muQ,128)); | |||||
assertEquals(pConverted, p); | |||||
assertEquals(pSquaredConverted, pSquared); | |||||
assertEquals(qConverted, q); | |||||
assertEquals(qSquaredConverted, qSquared); | |||||
assertEquals(pInverseConverted, pInverse); | |||||
assertEquals(muPConverted, muP); | |||||
assertEquals(muQConverted, muQ); | |||||
} | |||||
@Test | |||||
public void encryptTest() { | |||||
AsymmetricCipherKeyPair keyPair = PaillierUtils.generateKeyPair(); | |||||
PaillierPublicKeyParameters pubKeyParams = (PaillierPublicKeyParameters) keyPair.getPublic(); | |||||
byte[] pubKeyBytes = PaillierUtils.pubKey2Bytes(pubKeyParams); | |||||
SecureRandom random = new SecureRandom(); | |||||
byte[] data = new byte[256]; | |||||
random.nextBytes(data); | |||||
byte[] ciphertextFromParams = PaillierUtils.encrypt(data,pubKeyParams); | |||||
byte[] ciphertextFromBytes = PaillierUtils.encrypt(data,pubKeyBytes); | |||||
assertEquals(512,ciphertextFromParams.length); | |||||
assertEquals(512,ciphertextFromBytes.length); | |||||
} | |||||
@Test | |||||
public void decryptTest(){ | |||||
AsymmetricCipherKeyPair keyPair = PaillierUtils.generateKeyPair(); | |||||
PaillierPublicKeyParameters pubKeyParams = (PaillierPublicKeyParameters) keyPair.getPublic(); | |||||
PaillierPrivateKeyParameters privKeyParams = (PaillierPrivateKeyParameters) keyPair.getPrivate(); | |||||
byte[] pubKeyBytes = PaillierUtils.pubKey2Bytes(pubKeyParams); | |||||
byte[] privKeyBytes = PaillierUtils.privKey2Bytes(privKeyParams); | |||||
int input = 666; | |||||
byte[] data = intToByteArray(input); | |||||
byte[] ciphertextFromParams = PaillierUtils.encrypt(data,pubKeyParams); | |||||
byte[] ciphertextFromBytes = PaillierUtils.encrypt(data,pubKeyBytes); | |||||
byte[] plaintextFromParams = PaillierUtils.decrypt(ciphertextFromBytes,privKeyParams); | |||||
byte[] plaintextFromBytes = PaillierUtils.decrypt(ciphertextFromParams,privKeyBytes); | |||||
int outputFromParams = byteArrayToInt(plaintextFromParams); | |||||
int outputFromBytes = byteArrayToInt(plaintextFromBytes); | |||||
assertEquals(input,outputFromParams); | |||||
assertEquals(input,outputFromBytes); | |||||
} | |||||
@Test | |||||
public void addTest() { | |||||
AsymmetricCipherKeyPair keyPair = PaillierUtils.generateKeyPair(); | |||||
PaillierPublicKeyParameters pubKeyParams = (PaillierPublicKeyParameters) keyPair.getPublic(); | |||||
PaillierPrivateKeyParameters privKeyParams = (PaillierPrivateKeyParameters) keyPair.getPrivate(); | |||||
byte[] pubKeyBytes = PaillierUtils.pubKey2Bytes(pubKeyParams); | |||||
int input1 = 600; | |||||
int input2 = 60; | |||||
int input3 = 6; | |||||
int sum = 666; | |||||
byte[] data1 = intToByteArray(input1); | |||||
byte[] data2 = intToByteArray(input2); | |||||
byte[] data3 = intToByteArray(input3); | |||||
byte[] ciphertext1 = PaillierUtils.encrypt(data1,pubKeyParams); | |||||
byte[] ciphertext2 = PaillierUtils.encrypt(data2,pubKeyParams); | |||||
byte[] ciphertext3 = PaillierUtils.encrypt(data3,pubKeyParams); | |||||
byte[] aggregatedCiphertext = PaillierUtils.add(pubKeyParams,ciphertext1,ciphertext2,ciphertext3); | |||||
byte[] plaintext = PaillierUtils.decrypt(aggregatedCiphertext,privKeyParams); | |||||
int output = byteArrayToInt(plaintext); | |||||
assertEquals(sum,output); | |||||
aggregatedCiphertext = PaillierUtils.add(pubKeyBytes,ciphertext1,ciphertext2,ciphertext3); | |||||
plaintext = PaillierUtils.decrypt(aggregatedCiphertext,privKeyParams); | |||||
output = byteArrayToInt(plaintext); | |||||
assertEquals(sum,output); | |||||
} | |||||
@Test | |||||
public void scalarMultiplyTest() { | |||||
AsymmetricCipherKeyPair keyPair = PaillierUtils.generateKeyPair(); | |||||
PaillierPublicKeyParameters pubKeyParams = (PaillierPublicKeyParameters) keyPair.getPublic(); | |||||
PaillierPrivateKeyParameters privKeyParams = (PaillierPrivateKeyParameters) keyPair.getPrivate(); | |||||
byte[] pubKeyBytes = PaillierUtils.pubKey2Bytes(pubKeyParams); | |||||
int input = 111; | |||||
int scalar = 6; | |||||
byte[] data = intToByteArray(input); | |||||
byte[] ciphertext = PaillierUtils.encrypt(data,pubKeyParams); | |||||
byte[] ciphertextPowered = PaillierUtils.scalarMultiply(pubKeyBytes,ciphertext,scalar); | |||||
byte[] plaintextMultiplied = PaillierUtils.decrypt(ciphertextPowered,privKeyParams); | |||||
int output = byteArrayToInt(plaintextMultiplied); | |||||
assertEquals(input * scalar, output); | |||||
} | |||||
private static byte[] intToByteArray(int input) { | |||||
byte[] result = new byte[4]; | |||||
result[0] = (byte) ((input >> 24) & 0xFF); | |||||
result[1] = (byte) ((input >> 16) & 0xFF); | |||||
result[2] = (byte) ((input >> 8 ) & 0xFF); | |||||
result[3] = (byte) ((input ) & 0xFF); | |||||
return result; | |||||
} | |||||
private static int byteArrayToInt(byte[] input) { | |||||
int result; | |||||
int length = input.length; | |||||
byte[] buffer = new byte[4]; | |||||
if (length <= buffer.length){ | |||||
System.arraycopy(input,0,buffer,buffer.length - length,length); | |||||
} else { | |||||
System.arraycopy(input,length - buffer.length,buffer,0, buffer.length); | |||||
} | |||||
result = buffer[3] & 0xFF | | |||||
(buffer[2] & 0xFF) << 8 | | |||||
(buffer[1] & 0xFF) << 16 | | |||||
(buffer[0] & 0xFF) << 24; | |||||
return result; | |||||
} | |||||
// To convert BigInteger to byte array in specified size | |||||
private static byte[] bigIntegerToBytes(BigInteger b, int bytesSize){ | |||||
byte[] tmp = b.toByteArray(); | |||||
byte[] result = new byte[bytesSize]; | |||||
if (tmp.length > result.length) { | |||||
System.arraycopy(tmp, tmp.length - result.length, result, 0, result.length); | |||||
} | |||||
else { | |||||
System.arraycopy(tmp,0,result,result.length-tmp.length,tmp.length); | |||||
} | |||||
return result; | |||||
} | |||||
} |