Skip to content

Commit 1153425

Browse files
committed
Fix: add newline EOF, implement ElGamalCipher (clean), resolve linter issues
1 parent 69fe7d0 commit 1153425

File tree

2 files changed

+66
-185
lines changed

2 files changed

+66
-185
lines changed

src/main/java/com/thealgorithms/backtracking/SudokuSolver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,4 @@ public static void printBoard(int[][] board) {
143143
System.out.println();
144144
}
145145
}
146-
}git
146+
}

src/main/java/com/thealgorithms/ciphers/ElGamalCipher.java

Lines changed: 65 additions & 184 deletions
Original file line numberDiff line numberDiff line change
@@ -4,226 +4,107 @@
44
import java.security.SecureRandom;
55

66
/**
7-
* ElGamal Encryption Algorithm Implementation
7+
* Simple ElGamal implementation for educational purposes.
88
*
9-
* ElGamal is an asymmetric key encryption algorithm for public-key cryptography
10-
* based on the Discrete Logarithm Problem (DLP). It provides semantic security
11-
* through randomization in the encryption process.
9+
* <p>Note: This implementation is intended for educational / illustrative use in
10+
* algorithm libraries. Real-world cryptographic usage requires careful review,
11+
* authenticated encryption, parameter validation, constant-time implementations,
12+
* and using well-reviewed libraries.
1213
*
13-
* Key Components:
14-
* - p: Large prime number (modulus)
15-
* - g: Generator of the multiplicative group modulo p
16-
* - x: Private key (random integer)
17-
* - y: Public key where y = g^x mod p
14+
* <p>References:
15+
* https://en.wikipedia.org/wiki/ElGamal_encryption
1816
*
19-
* Encryption: For message m, choose random k and compute:
20-
* c1 = g^k mod p
21-
* c2 = m * y^k mod p
22-
* Ciphertext = (c1, c2)
23-
*
24-
* Decryption: Recover m using:
25-
* m = c2 * (c1^x)^-1 mod p
26-
* where (c1^x)^-1 is the modular multiplicative inverse
27-
*
28-
* Wikipedia: https://en.wikipedia.org/wiki/ElGamal_encryption
29-
*
30-
* @author TheAlgorithms
17+
* Author: TheAlgorithms
3118
*/
3219
public final class ElGamalCipher {
3320

21+
private static final SecureRandom RANDOM = new SecureRandom();
22+
3423
private ElGamalCipher() {
35-
// Utility class - prevent instantiation
24+
// Utility class
3625
}
3726

38-
/**
39-
* Represents an ElGamal key pair containing public and private keys
40-
*/
41-
public static class KeyPair {
42-
private final BigInteger p;
43-
private final BigInteger g;
44-
private final BigInteger x;
45-
private final BigInteger y;
27+
/** Public key container. */
28+
public static final class PublicKey {
29+
public final BigInteger p; // prime modulus
30+
public final BigInteger g; // generator
31+
public final BigInteger h; // h = g^x mod p
4632

47-
public KeyPair(BigInteger p, BigInteger g, BigInteger x, BigInteger y) {
33+
public PublicKey(final BigInteger p, final BigInteger g, final BigInteger h) {
4834
this.p = p;
4935
this.g = g;
50-
this.x = x;
51-
this.y = y;
52-
}
53-
54-
public BigInteger getP() {
55-
return p;
56-
}
57-
58-
public BigInteger getG() {
59-
return g;
36+
this.h = h;
6037
}
38+
}
6139

62-
public BigInteger getPrivateKey() {
63-
return x;
64-
}
40+
/** Private key container. */
41+
public static final class PrivateKey {
42+
public final BigInteger p;
43+
public final BigInteger x; // secret exponent
6544

66-
public BigInteger getPublicKey() {
67-
return y;
45+
public PrivateKey(final BigInteger p, final BigInteger x) {
46+
this.p = p;
47+
this.x = x;
6848
}
6949
}
7050

71-
/**
72-
* Represents an ElGamal ciphertext as a pair (c1, c2)
73-
*/
74-
public static class Ciphertext {
75-
private final BigInteger c1;
76-
private final BigInteger c2;
51+
/** Ciphertext pair (c1, c2). */
52+
public static final class CipherText {
53+
public final BigInteger c1;
54+
public final BigInteger c2;
7755

78-
public Ciphertext(BigInteger c1, BigInteger c2) {
56+
public CipherText(final BigInteger c1, final BigInteger c2) {
7957
this.c1 = c1;
8058
this.c2 = c2;
8159
}
82-
83-
public BigInteger getC1() {
84-
return c1;
85-
}
86-
87-
public BigInteger getC2() {
88-
return c2;
89-
}
90-
91-
@Override
92-
public String toString() {
93-
return "Ciphertext{c1=" + c1 + ", c2=" + c2 + "}";
94-
}
9560
}
9661

9762
/**
98-
* Generates ElGamal key pair with specified bit length
63+
* Generates an ElGamal keypair.
9964
*
100-
* Steps:
101-
* 1. Generate a large prime p
102-
* 2. Find a generator g of the multiplicative group mod p
103-
* 3. Choose random private key x in range [2, p-2]
104-
* 4. Compute public key y = g^x mod p
105-
*
106-
* @param bitLength The bit length for the prime (e.g., 512, 1024, 2048)
107-
* @return KeyPair containing (p, g, x, y)
65+
* @param bitLength size of prime modulus (e.g., 2048)
66+
* @return an array where [0]=PublicKey and [1]=PrivateKey
10867
*/
109-
public static KeyPair generateKeys(int bitLength) {
110-
SecureRandom random = new SecureRandom();
111-
112-
BigInteger p = BigInteger.probablePrime(bitLength, random);
113-
BigInteger g = findGenerator(p);
114-
115-
BigInteger x;
116-
do {
117-
x = new BigInteger(bitLength - 1, random);
118-
} while (x.compareTo(BigInteger.TWO) < 0 || x.compareTo(p.subtract(BigInteger.TWO)) > 0);
119-
120-
BigInteger y = g.modPow(x, p);
121-
122-
return new KeyPair(p, g, x, y);
68+
public static Object[] generateKeyPair(final int bitLength) {
69+
final BigInteger p = BigInteger.probablePrime(bitLength, RANDOM);
70+
// find a generator g in [2, p-2] (this simple approach picks a candidate; for
71+
// production use proper safe-prime/generator selection)
72+
final BigInteger g = BigInteger.valueOf(2);
73+
final BigInteger x = new BigInteger(bitLength - 2, RANDOM).mod(p.subtract(BigInteger.TWO)).add(BigInteger.ONE);
74+
final BigInteger h = g.modPow(x, p);
75+
final PublicKey pub = new PublicKey(p, g, h);
76+
final PrivateKey priv = new PrivateKey(p, x);
77+
return new Object[] {pub, priv};
12378
}
12479

12580
/**
126-
* Finds a generator for the multiplicative group modulo p
81+
* Encrypts a message m (0 < m < p).
12782
*
128-
* @param p The prime modulus
129-
* @return A generator g
83+
* @param m message as BigInteger (must be less than p)
84+
* @param pub the public key
85+
* @return ciphertext pair
13086
*/
131-
private static BigInteger findGenerator(BigInteger p) {
132-
BigInteger g = BigInteger.valueOf(2);
133-
134-
while (g.compareTo(p) < 0) {
135-
if (g.modPow(p.subtract(BigInteger.ONE), p).equals(BigInteger.ONE)) {
136-
return g;
137-
}
138-
g = g.add(BigInteger.ONE);
139-
}
140-
141-
return BigInteger.valueOf(2);
142-
}
143-
144-
/**
145-
* Encrypts a message using ElGamal encryption
146-
*
147-
* Process:
148-
* 1. Choose random k in range [2, p-2]
149-
* 2. Compute c1 = g^k mod p
150-
* 3. Compute c2 = m * y^k mod p
151-
* 4. Return ciphertext (c1, c2)
152-
*
153-
* @param message The plaintext message as BigInteger (must be < p)
154-
* @param keyPair The key pair containing public parameters
155-
* @return Ciphertext (c1, c2)
156-
*/
157-
public static Ciphertext encrypt(BigInteger message, KeyPair keyPair) {
158-
if (message.compareTo(keyPair.getP()) >= 0) {
159-
throw new IllegalArgumentException("Message must be less than modulus p");
87+
public static CipherText encrypt(final BigInteger m, final PublicKey pub) {
88+
if (m.compareTo(BigInteger.ZERO) <= 0 || m.compareTo(pub.p) >= 0) {
89+
throw new IllegalArgumentException("Message out of range");
16090
}
161-
162-
SecureRandom random = new SecureRandom();
163-
BigInteger p = keyPair.getP();
164-
BigInteger g = keyPair.getG();
165-
BigInteger y = keyPair.getPublicKey();
166-
167-
BigInteger k;
168-
do {
169-
k = new BigInteger(p.bitLength() - 1, random);
170-
} while (k.compareTo(BigInteger.TWO) < 0 || k.compareTo(p.subtract(BigInteger.TWO)) > 0);
171-
172-
BigInteger c1 = g.modPow(k, p);
173-
BigInteger c2 = message.multiply(y.modPow(k, p)).mod(p);
174-
175-
return new Ciphertext(c1, c2);
91+
final BigInteger y = new BigInteger(pub.p.bitLength() - 1, RANDOM).mod(pub.p.subtract(BigInteger.TWO)).add(BigInteger.ONE);
92+
final BigInteger c1 = pub.g.modPow(y, pub.p);
93+
final BigInteger s = pub.h.modPow(y, pub.p); // shared secret
94+
final BigInteger c2 = s.multiply(m).mod(pub.p);
95+
return new CipherText(c1, c2);
17696
}
17797

17898
/**
179-
* Decrypts a ciphertext using ElGamal decryption
180-
*
181-
* Process:
182-
* 1. Compute s = c1^x mod p (shared secret)
183-
* 2. Compute s^-1 (modular multiplicative inverse of s)
184-
* 3. Recover m = c2 * s^-1 mod p
99+
* Decrypts a ciphertext using the private key.
185100
*
186-
* @param ciphertext The ciphertext (c1, c2)
187-
* @param keyPair The key pair containing private key
188-
* @return Decrypted plaintext message
101+
* @param ct ciphertext
102+
* @param priv private key
103+
* @return decrypted message as BigInteger
189104
*/
190-
public static BigInteger decrypt(Ciphertext ciphertext, KeyPair keyPair) {
191-
BigInteger c1 = ciphertext.getC1();
192-
BigInteger c2 = ciphertext.getC2();
193-
BigInteger x = keyPair.getPrivateKey();
194-
BigInteger p = keyPair.getP();
195-
196-
BigInteger s = c1.modPow(x, p);
197-
BigInteger sInverse = s.modInverse(p);
198-
BigInteger message = c2.multiply(sInverse).mod(p);
199-
200-
return message;
201-
}
202-
203-
/**
204-
* Converts a string to BigInteger for encryption
205-
*
206-
* @param text The input string
207-
* @return BigInteger representation
208-
*/
209-
public static BigInteger stringToBigInteger(String text) {
210-
byte[] bytes = text.getBytes();
211-
return new BigInteger(1, bytes);
212-
}
213-
214-
/**
215-
* Converts BigInteger back to string after decryption
216-
*
217-
* @param number The BigInteger to convert
218-
* @return Original string
219-
*/
220-
public static String bigIntegerToString(BigInteger number) {
221-
byte[] bytes = number.toByteArray();
222-
if (bytes[0] == 0 && bytes.length > 1) {
223-
byte[] tmp = new byte[bytes.length - 1];
224-
System.arraycopy(bytes, 1, tmp, 0, tmp.length);
225-
bytes = tmp;
226-
}
227-
return new String(bytes);
105+
public static BigInteger decrypt(final CipherText ct, final PrivateKey priv) {
106+
final BigInteger s = ct.c1.modPow(priv.x, priv.p);
107+
final BigInteger sInv = s.modInverse(priv.p);
108+
return ct.c2.multiply(sInv).mod(priv.p);
228109
}
229-
}
110+
}

0 commit comments

Comments
 (0)