diff --git a/source/crypto/crypto-adv/pom.xml b/source/crypto/crypto-adv/pom.xml index ac72b043..bf759175 100644 --- a/source/crypto/crypto-adv/pom.xml +++ b/source/crypto/crypto-adv/pom.xml @@ -45,11 +45,5 @@ compile - - com.n1analytics - javallier_2.10 - 0.6.0 - - \ No newline at end of file diff --git a/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/mpc/MultiSum.java b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/mpc/MultiSum.java index 7fe64ef9..45392f29 100644 --- a/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/mpc/MultiSum.java +++ b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/mpc/MultiSum.java @@ -2,17 +2,15 @@ package com.jd.blockchain.crypto.mpc; 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.CipherParameters; import org.bouncycastle.crypto.agreement.ECDHBasicAgreement; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.math.ec.ECCurve; 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.SM3Utils; @@ -20,68 +18,73 @@ import com.jd.blockchain.utils.io.BytesUtils; 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(); - 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(); - 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); - 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 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){ byte[] ePubKeyX = new byte[32]; @@ -97,9 +100,9 @@ public class MultiSum { } // 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[] result = new byte[l]; + byte[] result = new byte[32]; if (tmp.length > result.length) { System.arraycopy(tmp, tmp.length - result.length, result, 0, result.length); } @@ -108,4 +111,10 @@ public class MultiSum { } return result; } + + // To retrieve the public key point from publicKey in byte array mode + private static ECPoint resolvePubKeyBytes(byte[] publicKey){ + return curve.decodePoint(publicKey); + } + } diff --git a/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PaillierKeyPairGenerator.java b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PaillierKeyPairGenerator.java new file mode 100644 index 00000000..abb72987 --- /dev/null +++ b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PaillierKeyPairGenerator.java @@ -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); + } +} diff --git a/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PaillierPrivateKeyParameters.java b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PaillierPrivateKeyParameters.java new file mode 100644 index 00000000..5dcb2f0f --- /dev/null +++ b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PaillierPrivateKeyParameters.java @@ -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; + } +} diff --git a/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PaillierPublicKeyParameters.java b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PaillierPublicKeyParameters.java new file mode 100644 index 00000000..3706b6b2 --- /dev/null +++ b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PaillierPublicKeyParameters.java @@ -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; + } +} diff --git a/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PaillierUtils.java b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PaillierUtils.java new file mode 100644 index 00000000..3d765df3 --- /dev/null +++ b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PaillierUtils.java @@ -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; + } + } + } +} diff --git a/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/mpc/MultiSumTest.java b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/mpc/MultiSumTest.java index 2bb53407..6c04a74a 100644 --- a/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/mpc/MultiSumTest.java +++ b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/mpc/MultiSumTest.java @@ -1,103 +1,88 @@ package test.com.jd.blockchain.crypto.mpc; 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 java.math.BigInteger; - import static org.junit.Assert.*; public class MultiSumTest { - private PaillierPrivateKey decKey; - private PaillierPublicKey encKey; - - @Before - public void init() { - decKey = PaillierPrivateKey.create(2048); - encKey = decKey.getPublicKey(); - } - @Test 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[] id2 = "2".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; } } \ No newline at end of file diff --git a/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/PaillierUtilsTest.java b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/PaillierUtilsTest.java new file mode 100644 index 00000000..8ace44d1 --- /dev/null +++ b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/PaillierUtilsTest.java @@ -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; + } +}