1 package com.google.privacy.private_join_and_compute.encryption.commutative; 2 3 import com.google.common.annotations.VisibleForTesting; 4 import com.google.common.hash.Hashing; 5 import com.google.common.primitives.Bytes; 6 import java.math.BigInteger; 7 import java.security.KeyFactory; 8 import java.security.KeyPairGenerator; 9 import java.security.NoSuchAlgorithmException; 10 import java.security.SecureRandom; 11 import java.security.interfaces.ECPrivateKey; 12 import java.security.spec.ECParameterSpec; 13 import java.security.spec.ECPoint; 14 import java.security.spec.ECPrivateKeySpec; 15 import java.security.spec.InvalidKeySpecException; 16 17 /** 18 * EcCommutativeCipher class with the property that K1(K2(a)) = K2(K1(a)) where K(a) is encryption 19 * with the key K. 20 * 21 * <p>This class allows two parties to determine if they share the same value, without revealing the 22 * sensitive value to each other. See the paper "Using Commutative Encryption to Share a Secret" at 23 * https://eprint.iacr.org/2008/356.pdf for reference. 24 * 25 * <p>The encryption is performed over an elliptic curve. 26 * 27 * <p>Security: The provided bit security is half the number of bits of the underlying curve. For 28 * example, using curve secp160r1 gives 80 bit security. 29 */ 30 public abstract class EcCommutativeCipherBase { 31 /** List of supported underlying hash types for the commutative cipher. */ 32 public enum HashType { 33 SHA256(256), 34 SHA384(384), 35 SHA512(512); 36 37 private final int hashBitLength; 38 HashType(int hashBitLength)39 private HashType(int hashBitLength) { 40 this.hashBitLength = hashBitLength; 41 } 42 43 /** Returns the bit length. */ getHashBitLength()44 public int getHashBitLength() { 45 return hashBitLength; 46 } 47 } 48 49 // EC classes are conceptually immutable even though the class is not annotated accordingly. 50 @SuppressWarnings("Immutable") 51 protected final ECPrivateKey privateKey; 52 53 @SuppressWarnings("Immutable") 54 protected final SupportedCurve ecCurve; 55 56 protected final HashType hashType; 57 58 /** Creates an EcCommutativeCipherBase object with the given private key and curve. */ EcCommutativeCipherBase(HashType hashType, ECPrivateKey key, SupportedCurve ecCurve)59 protected EcCommutativeCipherBase(HashType hashType, ECPrivateKey key, SupportedCurve ecCurve) { 60 this.privateKey = key; 61 this.ecCurve = ecCurve; 62 this.hashType = hashType; 63 } 64 65 /** Decodes the private key from BigInteger. */ decodePrivateKey(BigInteger key, SupportedCurve curve)66 protected static ECPrivateKey decodePrivateKey(BigInteger key, SupportedCurve curve) 67 throws InvalidKeySpecException { 68 checkPrivateKey(key, curve.getParameterSpec()); 69 ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(key, curve.getParameterSpec()); 70 try { 71 KeyFactory keyFactory = KeyFactory.getInstance("EC"); 72 return (ECPrivateKey) keyFactory.generatePrivate(privateKeySpec); 73 } catch (NoSuchAlgorithmException e) { 74 throw new AssertionError(e); 75 } 76 } 77 78 /** Creates a new random private key. */ createPrivateKey(SupportedCurve curve)79 protected static ECPrivateKey createPrivateKey(SupportedCurve curve) { 80 try { 81 KeyPairGenerator generator = KeyPairGenerator.getInstance("EC"); 82 generator.initialize(curve.getParameterSpec(), new SecureRandom()); 83 return (ECPrivateKey) generator.generateKeyPair().getPrivate(); 84 } catch (Exception e) { 85 throw new AssertionError(e); 86 } 87 } 88 89 /** 90 * Encrypts an input with the private key, first hashing the input to the curve. 91 * 92 * @param plaintext bytes to encrypt 93 * @return an encoded point in compressed form as defined in ANSI X9.62 ECDSA. 94 */ encrypt(byte[] plaintext)95 public abstract byte[] encrypt(byte[] plaintext); 96 97 /** 98 * Re-encrypts an encoded point with the private key. 99 * 100 * @param ciphertext an encoded point as defined in ANSI X9.62 ECDSA 101 * @return an encoded point in compressed form as defined in ANSI X9.62 ECDSA 102 */ reEncrypt(byte[] ciphertext)103 public abstract byte[] reEncrypt(byte[] ciphertext); 104 105 /** 106 * Decrypts an encoded point that has been previously encrypted with the private key. Does not 107 * reverse hashing to the curve. 108 * 109 * @param ciphertext an encoded point as defined in ANSI X9.62 ECDSA 110 * @return an encoded point in compressed form as defined in ANSI X9.62 ECDSA 111 */ decrypt(byte[] ciphertext)112 public abstract byte[] decrypt(byte[] ciphertext); 113 114 /** 115 * Hashes bytes to a point on the elliptic curve y^2 = x^3 + ax + b over a prime field. 116 */ 117 @VisibleForTesting hashIntoTheCurveInternal(byte[] byteId)118 abstract ECPoint hashIntoTheCurveInternal(byte[] byteId); 119 120 /** 121 * Hashes bytes to a point on the elliptic curve y^2 = x^3 + ax + b over a prime field. All 122 * implementations must match the C++ version: 123 * 124 * <p>The resulting point is returned encoded in compressed form as defined in ANSI X9.62 ECDSA. 125 */ hashIntoTheCurve(byte[] byteId)126 public abstract byte[] hashIntoTheCurve(byte[] byteId); 127 128 /** 129 * A random oracle function mapping x deterministically into a large domain. 130 * 131 * <p>// copybara:strip_begin(Remove internal comment) 132 * 133 * <p>The random oracle is similar to the example given in the last paragraph of Chapter 6 of [1] 134 * where the output is expanded by successively hashing the concatenation of the input with a 135 * fixed sized counter starting from 1. 136 * 137 * <p>[1] Bellare, Mihir, and Phillip Rogaway. "Random oracles are practical: A paradigm for 138 * designing efficient protocols." Proceedings of the 1st ACM conference on Computer and 139 * communications security. ACM, 1993. 140 * 141 * <p>Returns a value from the set [0, max_value). 142 * 143 * <p>Check Error: if bit length of max_value is greater than 130048. Since the counter used for 144 * expanding the output is expanded to 8 bit length (hard-coded), any counter value that is 145 * greater than 512 would cause variable length inputs passed to the underlying 146 * sha256/sha384/sha512 calls and might make this random oracle's output not uniform across the 147 * output domain. 148 * 149 * <p>The output length is increased by a security value of 256 which reduces the bias of 150 * selecting certain values more often than others when max_value is not a multiple of 2. 151 */ randomOracle(byte[] bytes, BigInteger maxValue, HashType hashType)152 public static BigInteger randomOracle(byte[] bytes, BigInteger maxValue, HashType hashType) { 153 int hashBitLength = hashType.getHashBitLength(); 154 int outputBitLength = maxValue.bitLength() + hashBitLength; 155 int iterCount = (outputBitLength + hashBitLength - 1) / hashBitLength; 156 int excessBitCount = (iterCount * hashBitLength) - outputBitLength; 157 BigInteger hashOutput = BigInteger.ZERO; 158 BigInteger counter = BigInteger.ONE; 159 for (int i = 1; i < iterCount + 1; ++i) { 160 hashOutput = hashOutput.shiftLeft(hashBitLength); 161 byte[] counterBytes = bigIntegerToByteArrayCppCompatible(counter); 162 byte[] hashInput = Bytes.concat(counterBytes, bytes); 163 byte[] hashCode; 164 switch (hashType) { 165 case SHA256: 166 hashCode = Hashing.sha256().hashBytes(hashInput).asBytes(); 167 break; 168 case SHA384: 169 hashCode = Hashing.sha384().hashBytes(hashInput).asBytes(); 170 break; 171 default: 172 hashCode = Hashing.sha512().hashBytes(hashInput).asBytes(); 173 } 174 hashOutput = hashOutput.add(byteArrayToBigIntegerCppCompatible(hashCode)); 175 counter = counter.add(BigInteger.ONE); 176 } 177 return hashOutput.shiftRight(excessBitCount).mod(maxValue); 178 } 179 180 /** Checks the private key is between 1 and the order of the group. */ checkPrivateKey(BigInteger key, ECParameterSpec params)181 private static void checkPrivateKey(BigInteger key, ECParameterSpec params) { 182 if (key.compareTo(BigInteger.ONE) <= 0 || key.compareTo(params.getOrder()) >= 0) { 183 throw new IllegalArgumentException("The given key is out of bounds."); 184 } 185 } 186 187 /** 188 * Returns the private key bytes. 189 * 190 * @return the private key bytes for this EcCommutativeCipher. 191 */ getPrivateKeyBytes()192 public byte[] getPrivateKeyBytes() { 193 return bigIntegerToByteArrayCppCompatible(privateKey.getS()); 194 } 195 196 // copybara:strip_begin(Remove deprecated functions) 197 /** 198 * Returns the private key bytes. 199 * 200 * @deprecated This function is incompatible with the C++ implementation. 201 * @return the private key bytes for this EcCommutativeCipher. 202 */ 203 @Deprecated getPrivateKeyBytesCppIncompatible()204 public byte[] getPrivateKeyBytesCppIncompatible() { 205 return privateKey.getS().toByteArray(); 206 } 207 // copybara:strip_end 208 209 /** 210 * This function converts a BigInteger into a byte array in big-endian form without two's 211 * complement representation. This function is compatible with C++ OpenSSL's BigNum 212 * implementation. 213 */ bigIntegerToByteArrayCppCompatible(BigInteger value)214 public static byte[] bigIntegerToByteArrayCppCompatible(BigInteger value) { 215 byte[] signedArray = value.toByteArray(); 216 int leadingZeroes = 0; 217 while (signedArray[leadingZeroes] == 0) { 218 leadingZeroes++; 219 } 220 byte[] unsignedArray = new byte[signedArray.length - leadingZeroes]; 221 System.arraycopy(signedArray, leadingZeroes, unsignedArray, 0, unsignedArray.length); 222 return unsignedArray; 223 } 224 225 /** 226 * This function converts bytes to BigInteger. The input bytes are assumed to be in big-endian 227 * form. The function converts the bytes into two's complement big-endian form before converting 228 * into a BigInteger. This function matches the C++ OpenSSL implementation of bytes to BigNum. 229 */ byteArrayToBigIntegerCppCompatible(byte[] bytes)230 public static BigInteger byteArrayToBigIntegerCppCompatible(byte[] bytes) { 231 byte[] twosComplement = new byte[bytes.length + 1]; 232 twosComplement[0] = 0; 233 System.arraycopy(bytes, 0, twosComplement, 1, bytes.length); 234 return new BigInteger(twosComplement); 235 } 236 237 /** 238 * Encodes a point. 239 * 240 * @param point a point to encode 241 * @return an encoded point in compressed form as defined in ANSI X9.62 ECDSA. 242 */ 243 @VisibleForTesting getEncoded(ECPoint point)244 abstract byte[] getEncoded(ECPoint point); 245 246 /** 247 * Checks validity of a point. 248 * 249 * @param point a point to check 250 * @return true iff point is valid. 251 */ 252 @VisibleForTesting isValid(ECPoint point)253 abstract boolean isValid(ECPoint point); 254 255 /** 256 * Checks whether a point is at infinity. 257 * 258 * @param point a point to check 259 * @return true iff point is infinity. 260 */ 261 @VisibleForTesting isInfinity(ECPoint point)262 abstract boolean isInfinity(ECPoint point); 263 } 264 265