| package org.bouncycastle.crypto.kems; |
| |
| import java.math.BigInteger; |
| import java.security.SecureRandom; |
| |
| import org.bouncycastle.crypto.CipherParameters; |
| import org.bouncycastle.crypto.DerivationFunction; |
| import org.bouncycastle.crypto.KeyEncapsulation; |
| import org.bouncycastle.crypto.params.ECDomainParameters; |
| import org.bouncycastle.crypto.params.ECKeyParameters; |
| import org.bouncycastle.crypto.params.ECPrivateKeyParameters; |
| import org.bouncycastle.crypto.params.ECPublicKeyParameters; |
| import org.bouncycastle.crypto.params.KDFParameters; |
| import org.bouncycastle.crypto.params.KeyParameter; |
| import org.bouncycastle.math.ec.ECCurve; |
| import org.bouncycastle.math.ec.ECMultiplier; |
| import org.bouncycastle.math.ec.ECPoint; |
| import org.bouncycastle.math.ec.FixedPointCombMultiplier; |
| import org.bouncycastle.util.Arrays; |
| import org.bouncycastle.util.BigIntegers; |
| |
| /** |
| * The ECIES Key Encapsulation Mechanism (ECIES-KEM) from ISO 18033-2. |
| */ |
| public class ECIESKeyEncapsulation |
| implements KeyEncapsulation |
| { |
| private static final BigInteger ONE = BigInteger.valueOf(1); |
| |
| private DerivationFunction kdf; |
| private SecureRandom rnd; |
| private ECKeyParameters key; |
| private boolean CofactorMode; |
| private boolean OldCofactorMode; |
| private boolean SingleHashMode; |
| |
| /** |
| * Set up the ECIES-KEM. |
| * |
| * @param kdf the key derivation function to be used. |
| * @param rnd the random source for the session key. |
| */ |
| public ECIESKeyEncapsulation( |
| DerivationFunction kdf, |
| SecureRandom rnd) |
| { |
| this.kdf = kdf; |
| this.rnd = rnd; |
| this.CofactorMode = false; |
| this.OldCofactorMode = false; |
| this.SingleHashMode = false; |
| } |
| |
| /** |
| * Set up the ECIES-KEM. |
| * |
| * @param kdf the key derivation function to be used. |
| * @param rnd the random source for the session key. |
| * @param cofactorMode if true use the new cofactor ECDH. |
| * @param oldCofactorMode if true use the old cofactor ECDH. |
| * @param singleHashMode if true use single hash mode. |
| */ |
| public ECIESKeyEncapsulation( |
| DerivationFunction kdf, |
| SecureRandom rnd, |
| boolean cofactorMode, |
| boolean oldCofactorMode, |
| boolean singleHashMode) |
| { |
| this.kdf = kdf; |
| this.rnd = rnd; |
| |
| // If both cofactorMode and oldCofactorMode are set to true |
| // then the implementation will use the new cofactor ECDH |
| this.CofactorMode = cofactorMode; |
| this.OldCofactorMode = oldCofactorMode; |
| this.SingleHashMode = singleHashMode; |
| } |
| |
| /** |
| * Initialise the ECIES-KEM. |
| * |
| * @param key the recipient's public (for encryption) or private (for decryption) key. |
| */ |
| public void init(CipherParameters key) |
| throws IllegalArgumentException |
| { |
| if (!(key instanceof ECKeyParameters)) |
| { |
| throw new IllegalArgumentException("EC key required"); |
| } |
| else |
| { |
| this.key = (ECKeyParameters)key; |
| } |
| } |
| |
| /** |
| * Generate and encapsulate a random session key. |
| * |
| * @param out the output buffer for the encapsulated key. |
| * @param outOff the offset for the output buffer. |
| * @param keyLen the length of the session key. |
| * @return the random session key. |
| */ |
| public CipherParameters encrypt(byte[] out, int outOff, int keyLen) |
| throws IllegalArgumentException |
| { |
| if (!(key instanceof ECPublicKeyParameters)) |
| { |
| throw new IllegalArgumentException("Public key required for encryption"); |
| } |
| |
| ECPublicKeyParameters ecPubKey = (ECPublicKeyParameters)key; |
| ECDomainParameters ecParams = ecPubKey.getParameters(); |
| ECCurve curve = ecParams.getCurve(); |
| BigInteger n = ecParams.getN(); |
| BigInteger h = ecParams.getH(); |
| |
| // Generate the ephemeral key pair |
| BigInteger r = BigIntegers.createRandomInRange(ONE, n, rnd); |
| |
| // Compute the static-ephemeral key agreement |
| BigInteger rPrime = CofactorMode ? r.multiply(h).mod(n) : r; |
| |
| ECMultiplier basePointMultiplier = createBasePointMultiplier(); |
| |
| ECPoint[] ghTilde = new ECPoint[]{ |
| basePointMultiplier.multiply(ecParams.getG(), r), |
| ecPubKey.getQ().multiply(rPrime) |
| }; |
| |
| // NOTE: More efficient than normalizing each individually |
| curve.normalizeAll(ghTilde); |
| |
| ECPoint gTilde = ghTilde[0], hTilde = ghTilde[1]; |
| |
| // Encode the ephemeral public key |
| byte[] C = gTilde.getEncoded(false); |
| System.arraycopy(C, 0, out, outOff, C.length); |
| |
| // Encode the shared secret value |
| byte[] PEH = hTilde.getAffineXCoord().getEncoded(); |
| |
| return deriveKey(keyLen, C, PEH); |
| } |
| |
| /** |
| * Generate and encapsulate a random session key. |
| * |
| * @param out the output buffer for the encapsulated key. |
| * @param keyLen the length of the session key. |
| * @return the random session key. |
| */ |
| public CipherParameters encrypt(byte[] out, int keyLen) |
| { |
| return encrypt(out, 0, keyLen); |
| } |
| |
| /** |
| * Decrypt an encapsulated session key. |
| * |
| * @param in the input buffer for the encapsulated key. |
| * @param inOff the offset for the input buffer. |
| * @param inLen the length of the encapsulated key. |
| * @param keyLen the length of the session key. |
| * @return the session key. |
| */ |
| public CipherParameters decrypt(byte[] in, int inOff, int inLen, int keyLen) |
| throws IllegalArgumentException |
| { |
| if (!(key instanceof ECPrivateKeyParameters)) |
| { |
| throw new IllegalArgumentException("Private key required for encryption"); |
| } |
| |
| ECPrivateKeyParameters ecPrivKey = (ECPrivateKeyParameters)key; |
| ECDomainParameters ecParams = ecPrivKey.getParameters(); |
| ECCurve curve = ecParams.getCurve(); |
| BigInteger n = ecParams.getN(); |
| BigInteger h = ecParams.getH(); |
| |
| // Decode the ephemeral public key |
| byte[] C = new byte[inLen]; |
| System.arraycopy(in, inOff, C, 0, inLen); |
| |
| // NOTE: Decoded points are already normalized (i.e in affine form) |
| ECPoint gTilde = curve.decodePoint(C); |
| |
| // Compute the static-ephemeral key agreement |
| ECPoint gHat = gTilde; |
| if ((CofactorMode) || (OldCofactorMode)) |
| { |
| gHat = gHat.multiply(h); |
| } |
| |
| BigInteger xHat = ecPrivKey.getD(); |
| if (CofactorMode) |
| { |
| xHat = xHat.multiply(h.modInverse(n)).mod(n); |
| } |
| |
| ECPoint hTilde = gHat.multiply(xHat).normalize(); |
| |
| // Encode the shared secret value |
| byte[] PEH = hTilde.getAffineXCoord().getEncoded(); |
| |
| return deriveKey(keyLen, C, PEH); |
| } |
| |
| /** |
| * Decrypt an encapsulated session key. |
| * |
| * @param in the input buffer for the encapsulated key. |
| * @param keyLen the length of the session key. |
| * @return the session key. |
| */ |
| public CipherParameters decrypt(byte[] in, int keyLen) |
| { |
| return decrypt(in, 0, in.length, keyLen); |
| } |
| |
| protected ECMultiplier createBasePointMultiplier() |
| { |
| return new FixedPointCombMultiplier(); |
| } |
| |
| protected KeyParameter deriveKey(int keyLen, byte[] C, byte[] PEH) |
| { |
| byte[] kdfInput = PEH; |
| if (!SingleHashMode) |
| { |
| kdfInput = Arrays.concatenate(C, PEH); |
| Arrays.fill(PEH, (byte)0); |
| } |
| |
| try |
| { |
| // Initialise the KDF |
| kdf.init(new KDFParameters(kdfInput, null)); |
| |
| // Generate the secret key |
| byte[] K = new byte[keyLen]; |
| kdf.generateBytes(K, 0, K.length); |
| |
| // Return the ciphertext |
| return new KeyParameter(K); |
| } |
| finally |
| { |
| Arrays.fill(kdfInput, (byte)0); |
| } |
| } |
| } |