diff options
Diffstat (limited to 'libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/ElGamalEngine.java')
-rw-r--r-- | libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/ElGamalEngine.java | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/ElGamalEngine.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/ElGamalEngine.java new file mode 100644 index 000000000..3a2939bad --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/engines/ElGamalEngine.java @@ -0,0 +1,217 @@ +package org.spongycastle.crypto.engines; + +import org.spongycastle.crypto.AsymmetricBlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.params.ElGamalKeyParameters; +import org.spongycastle.crypto.params.ElGamalPrivateKeyParameters; +import org.spongycastle.crypto.params.ElGamalPublicKeyParameters; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.util.BigIntegers; + +import java.math.BigInteger; +import java.security.SecureRandom; + +/** + * this does your basic ElGamal algorithm. + */ +public class ElGamalEngine + implements AsymmetricBlockCipher +{ + private ElGamalKeyParameters key; + private SecureRandom random; + private boolean forEncryption; + private int bitSize; + + private static final BigInteger ZERO = BigInteger.valueOf(0); + private static final BigInteger ONE = BigInteger.valueOf(1); + private static final BigInteger TWO = BigInteger.valueOf(2); + + /** + * initialise the ElGamal engine. + * + * @param forEncryption true if we are encrypting, false otherwise. + * @param param the necessary ElGamal key parameters. + */ + public void init( + boolean forEncryption, + CipherParameters param) + { + if (param instanceof ParametersWithRandom) + { + ParametersWithRandom p = (ParametersWithRandom)param; + + this.key = (ElGamalKeyParameters)p.getParameters(); + this.random = p.getRandom(); + } + else + { + this.key = (ElGamalKeyParameters)param; + this.random = new SecureRandom(); + } + + this.forEncryption = forEncryption; + + BigInteger p = key.getParameters().getP(); + + bitSize = p.bitLength(); + + if (forEncryption) + { + if (!(key instanceof ElGamalPublicKeyParameters)) + { + throw new IllegalArgumentException("ElGamalPublicKeyParameters are required for encryption."); + } + } + else + { + if (!(key instanceof ElGamalPrivateKeyParameters)) + { + throw new IllegalArgumentException("ElGamalPrivateKeyParameters are required for decryption."); + } + } + } + + /** + * Return the maximum size for an input block to this engine. + * For ElGamal this is always one byte less than the size of P on + * encryption, and twice the length as the size of P on decryption. + * + * @return maximum size for an input block. + */ + public int getInputBlockSize() + { + if (forEncryption) + { + return (bitSize - 1) / 8; + } + + return 2 * ((bitSize + 7) / 8); + } + + /** + * Return the maximum size for an output block to this engine. + * For ElGamal this is always one byte less than the size of P on + * decryption, and twice the length as the size of P on encryption. + * + * @return maximum size for an output block. + */ + public int getOutputBlockSize() + { + if (forEncryption) + { + return 2 * ((bitSize + 7) / 8); + } + + return (bitSize - 1) / 8; + } + + /** + * Process a single block using the basic ElGamal algorithm. + * + * @param in the input array. + * @param inOff the offset into the input buffer where the data starts. + * @param inLen the length of the data to be processed. + * @return the result of the ElGamal process. + * @exception DataLengthException the input block is too large. + */ + public byte[] processBlock( + byte[] in, + int inOff, + int inLen) + { + if (key == null) + { + throw new IllegalStateException("ElGamal engine not initialised"); + } + + int maxLength = forEncryption + ? (bitSize - 1 + 7) / 8 + : getInputBlockSize(); + + if (inLen > maxLength) + { + throw new DataLengthException("input too large for ElGamal cipher.\n"); + } + + BigInteger p = key.getParameters().getP(); + + if (key instanceof ElGamalPrivateKeyParameters) // decryption + { + byte[] in1 = new byte[inLen / 2]; + byte[] in2 = new byte[inLen / 2]; + + System.arraycopy(in, inOff, in1, 0, in1.length); + System.arraycopy(in, inOff + in1.length, in2, 0, in2.length); + + BigInteger gamma = new BigInteger(1, in1); + BigInteger phi = new BigInteger(1, in2); + + ElGamalPrivateKeyParameters priv = (ElGamalPrivateKeyParameters)key; + // a shortcut, which generally relies on p being prime amongst other things. + // if a problem with this shows up, check the p and g values! + BigInteger m = gamma.modPow(p.subtract(ONE).subtract(priv.getX()), p).multiply(phi).mod(p); + + return BigIntegers.asUnsignedByteArray(m); + } + else // encryption + { + byte[] block; + if (inOff != 0 || inLen != in.length) + { + block = new byte[inLen]; + + System.arraycopy(in, inOff, block, 0, inLen); + } + else + { + block = in; + } + + BigInteger input = new BigInteger(1, block); + + if (input.bitLength() >= p.bitLength()) + { + throw new DataLengthException("input too large for ElGamal cipher.\n"); + } + + ElGamalPublicKeyParameters pub = (ElGamalPublicKeyParameters)key; + + int pBitLength = p.bitLength(); + BigInteger k = new BigInteger(pBitLength, random); + + while (k.equals(ZERO) || (k.compareTo(p.subtract(TWO)) > 0)) + { + k = new BigInteger(pBitLength, random); + } + + BigInteger g = key.getParameters().getG(); + BigInteger gamma = g.modPow(k, p); + BigInteger phi = input.multiply(pub.getY().modPow(k, p)).mod(p); + + byte[] out1 = gamma.toByteArray(); + byte[] out2 = phi.toByteArray(); + byte[] output = new byte[this.getOutputBlockSize()]; + + if (out1.length > output.length / 2) + { + System.arraycopy(out1, 1, output, output.length / 2 - (out1.length - 1), out1.length - 1); + } + else + { + System.arraycopy(out1, 0, output, output.length / 2 - out1.length, out1.length); + } + + if (out2.length > output.length / 2) + { + System.arraycopy(out2, 1, output, output.length - (out2.length - 1), out2.length - 1); + } + else + { + System.arraycopy(out2, 0, output, output.length - out2.length, out2.length); + } + + return output; + } + } +} |