001: package org.bouncycastle.crypto.engines;
002:
003: import org.bouncycastle.crypto.AsymmetricBlockCipher;
004: import org.bouncycastle.crypto.CipherParameters;
005: import org.bouncycastle.crypto.DataLengthException;
006: import org.bouncycastle.crypto.params.ElGamalKeyParameters;
007: import org.bouncycastle.crypto.params.ElGamalPrivateKeyParameters;
008: import org.bouncycastle.crypto.params.ElGamalPublicKeyParameters;
009: import org.bouncycastle.crypto.params.ParametersWithRandom;
010: import org.bouncycastle.util.BigIntegers;
011:
012: import java.math.BigInteger;
013: import java.security.SecureRandom;
014:
015: /**
016: * this does your basic ElGamal algorithm.
017: */
018: public class ElGamalEngine implements AsymmetricBlockCipher {
019: private ElGamalKeyParameters key;
020: private SecureRandom random;
021: private boolean forEncryption;
022: private int bitSize;
023:
024: private static final BigInteger ZERO = BigInteger.valueOf(0);
025: private static final BigInteger ONE = BigInteger.valueOf(1);
026: private static final BigInteger TWO = BigInteger.valueOf(2);
027:
028: /**
029: * initialise the ElGamal engine.
030: *
031: * @param forEncryption true if we are encrypting, false otherwise.
032: * @param param the necessary ElGamal key parameters.
033: */
034: public void init(boolean forEncryption, CipherParameters param) {
035: if (param instanceof ParametersWithRandom) {
036: ParametersWithRandom p = (ParametersWithRandom) param;
037:
038: this .key = (ElGamalKeyParameters) p.getParameters();
039: this .random = p.getRandom();
040: } else {
041: this .key = (ElGamalKeyParameters) param;
042: this .random = new SecureRandom();
043: }
044:
045: this .forEncryption = forEncryption;
046:
047: bitSize = key.getParameters().getP().bitLength();
048:
049: if (forEncryption) {
050: if (!(key instanceof ElGamalPublicKeyParameters)) {
051: throw new IllegalArgumentException(
052: "ElGamalPublicKeyParameters are required for encryption.");
053: }
054: } else {
055: if (!(key instanceof ElGamalPrivateKeyParameters)) {
056: throw new IllegalArgumentException(
057: "ElGamalPrivateKeyParameters are required for decryption.");
058: }
059: }
060: }
061:
062: /**
063: * Return the maximum size for an input block to this engine.
064: * For ElGamal this is always one byte less than the size of P on
065: * encryption, and twice the length as the size of P on decryption.
066: *
067: * @return maximum size for an input block.
068: */
069: public int getInputBlockSize() {
070: if (forEncryption) {
071: return (bitSize - 1) / 8;
072: }
073:
074: return 2 * ((bitSize + 7) / 8);
075: }
076:
077: /**
078: * Return the maximum size for an output block to this engine.
079: * For ElGamal this is always one byte less than the size of P on
080: * decryption, and twice the length as the size of P on encryption.
081: *
082: * @return maximum size for an output block.
083: */
084: public int getOutputBlockSize() {
085: if (forEncryption) {
086: return 2 * ((bitSize + 7) / 8);
087: }
088:
089: return (bitSize - 1) / 8;
090: }
091:
092: /**
093: * Process a single block using the basic ElGamal algorithm.
094: *
095: * @param in the input array.
096: * @param inOff the offset into the input buffer where the data starts.
097: * @param inLen the length of the data to be processed.
098: * @return the result of the ElGamal process.
099: * @exception DataLengthException the input block is too large.
100: */
101: public byte[] processBlock(byte[] in, int inOff, int inLen) {
102: if (key == null) {
103: throw new IllegalStateException(
104: "ElGamal engine not initialised");
105: }
106:
107: int maxLength = forEncryption ? (bitSize - 1 + 7) / 8
108: : getInputBlockSize();
109:
110: if (inLen > maxLength) {
111: throw new DataLengthException(
112: "input too large for ElGamal cipher.\n");
113: }
114:
115: BigInteger p = key.getParameters().getP();
116:
117: if (key instanceof ElGamalPrivateKeyParameters) // decryption
118: {
119: byte[] in1 = new byte[inLen / 2];
120: byte[] in2 = new byte[inLen / 2];
121:
122: System.arraycopy(in, inOff, in1, 0, in1.length);
123: System
124: .arraycopy(in, inOff + in1.length, in2, 0,
125: in2.length);
126:
127: BigInteger gamma = new BigInteger(1, in1);
128: BigInteger phi = new BigInteger(1, in2);
129:
130: ElGamalPrivateKeyParameters priv = (ElGamalPrivateKeyParameters) key;
131:
132: BigInteger m = gamma.modPow(
133: p.subtract(ONE).subtract(priv.getX()), p).multiply(
134: phi).mod(p);
135:
136: return BigIntegers.asUnsignedByteArray(m);
137: } else // encryption
138: {
139: byte[] block;
140: if (inOff != 0 || inLen != in.length) {
141: block = new byte[inLen];
142:
143: System.arraycopy(in, inOff, block, 0, inLen);
144: } else {
145: block = in;
146: }
147:
148: BigInteger input = new BigInteger(1, block);
149:
150: if (input.bitLength() >= p.bitLength()) {
151: throw new DataLengthException(
152: "input too large for ElGamal cipher.\n");
153: }
154:
155: ElGamalPublicKeyParameters pub = (ElGamalPublicKeyParameters) key;
156:
157: int pBitLength = p.bitLength();
158: BigInteger k = new BigInteger(pBitLength, random);
159:
160: while (k.equals(ZERO) || (k.compareTo(p.subtract(TWO)) > 0)) {
161: k = new BigInteger(pBitLength, random);
162: }
163:
164: BigInteger g = key.getParameters().getG();
165: BigInteger gamma = g.modPow(k, p);
166: BigInteger phi = input.multiply(pub.getY().modPow(k, p))
167: .mod(p);
168:
169: byte[] out1 = gamma.toByteArray();
170: byte[] out2 = phi.toByteArray();
171: byte[] output = new byte[this .getOutputBlockSize()];
172:
173: if (out1.length > output.length / 2) {
174: System.arraycopy(out1, 1, output, output.length / 2
175: - (out1.length - 1), out1.length - 1);
176: } else {
177: System.arraycopy(out1, 0, output, output.length / 2
178: - out1.length, out1.length);
179: }
180:
181: if (out2.length > output.length / 2) {
182: System.arraycopy(out2, 1, output, output.length
183: - (out2.length - 1), out2.length - 1);
184: } else {
185: System.arraycopy(out2, 0, output, output.length
186: - out2.length, out2.length);
187: }
188:
189: return output;
190: }
191: }
192: }
|