Skip to content

Commit 9381464

Browse files
committed
Add Elliptic Curve Cryptography Algorithm
Implementation of ECC encryption algorithm and unit test with java
1 parent 46bdd25 commit 9381464

File tree

2 files changed

+346
-0
lines changed

2 files changed

+346
-0
lines changed
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
package com.thealgorithms.ciphers;
2+
3+
import java.math.BigInteger;
4+
import java.security.SecureRandom;
5+
6+
/**
7+
* ECC - Elliptic Curve Cryptography
8+
* Elliptic Curve Cryptography is a public-key cryptography method that uses the algebraic structure of
9+
* elliptic curves over finite fields. ECC provides a higher level of security with smaller key sizes compared
10+
* to other public-key methods like RSA, making it particularly suitable for environments where computational
11+
* resources are limited, such as mobile devices and embedded systems.
12+
*
13+
* This class implements elliptic curve cryptography, providing encryption and decryption
14+
* functionalities based on public and private key pairs.
15+
*
16+
* @author xuyang
17+
*/
18+
public class ECC {
19+
20+
private BigInteger privateKey; // Private key used for decryption
21+
private ECPoint publicKey; // Public key used for encryption
22+
private EllipticCurve curve; // Elliptic curve used in cryptography
23+
private ECPoint basePoint; // Base point G on the elliptic curve
24+
25+
public EllipticCurve getCurve() {
26+
return curve; // Returns the elliptic curve
27+
}
28+
29+
public ECC(int bits) {
30+
generateKeys(bits); // Generates public-private key pair
31+
}
32+
33+
// Getter and Setter for private key
34+
public BigInteger getPrivateKey() {
35+
return privateKey;
36+
}
37+
38+
public void setPrivateKey(BigInteger privateKey) {
39+
this.privateKey = privateKey;
40+
}
41+
42+
public void setCurve(EllipticCurve curve) {
43+
this.curve = curve;
44+
}
45+
46+
/**
47+
* Encrypts the message using the public key.
48+
* The message is transformed into an ECPoint and encrypted with elliptic curve operations.
49+
*
50+
* @param message The plain message to be encrypted
51+
* @return The encrypted message as an array of ECPoints (R, S)
52+
*/
53+
public synchronized ECPoint[] encrypt(String message) {
54+
BigInteger m = new BigInteger(message.getBytes()); // Convert message to BigInteger
55+
SecureRandom r = new SecureRandom(); // Generate random value for k
56+
BigInteger k = new BigInteger(curve.getFieldSize(), r); // Generate random scalar k
57+
58+
// Calculate point R = k * G, where G is the base point
59+
ECPoint R = basePoint.multiply(k, curve.getP(), curve.getA());
60+
61+
// Calculate point S = k * publicKey + encodedMessage
62+
ECPoint S = publicKey.multiply(k, curve.getP(), curve.getA()).add(curve.encodeMessage(m), curve.getP(), curve.getA());
63+
64+
return new ECPoint[] { R, S }; // Return encrypted message as two ECPoints
65+
}
66+
67+
/**
68+
* Decrypts the encrypted message using the private key.
69+
* The decryption process is the reverse of encryption, recovering the original message.
70+
*
71+
* @param encryptedMessage The encrypted message as an array of ECPoints (R, S)
72+
* @return The decrypted plain message as a String
73+
*/
74+
public synchronized String decrypt(ECPoint[] encryptedMessage) {
75+
ECPoint R = encryptedMessage[0]; // First part of ciphertext
76+
ECPoint S = encryptedMessage[1]; // Second part of ciphertext
77+
78+
// Perform decryption: S - R * privateKey
79+
ECPoint decodedMessage = S.subtract(R.multiply(privateKey, curve.getP(), curve.getA()), curve.getP(), curve.getA());
80+
81+
BigInteger m = curve.decodeMessage(decodedMessage); // Decode the message from ECPoint
82+
83+
return new String(m.toByteArray()); // Convert BigInteger back to String
84+
}
85+
86+
/**
87+
* Generates a new public-private key pair for encryption and decryption.
88+
*
89+
* @param bits The size (in bits) of the keys to generate
90+
*/
91+
public final synchronized void generateKeys(int bits) {
92+
SecureRandom r = new SecureRandom();
93+
curve = new EllipticCurve(bits); // Initialize a new elliptic curve
94+
basePoint = curve.getBasePoint(); // Set the base point G
95+
96+
// Generate private key as a random BigInteger
97+
privateKey = new BigInteger(bits, r);
98+
99+
// Generate public key as the point publicKey = privateKey * G
100+
publicKey = basePoint.multiply(privateKey, curve.getP(), curve.getA());
101+
}
102+
103+
/**
104+
* Class representing an elliptic curve with the form y^2 = x^3 + ax + b.
105+
*/
106+
public static class EllipticCurve {
107+
private final BigInteger a; // Coefficient a in the curve equation
108+
private final BigInteger b; // Coefficient b in the curve equation
109+
private final BigInteger p; // Prime number p, defining the finite field
110+
private final ECPoint basePoint; // Base point G on the curve
111+
112+
// Constructor with explicit parameters for a, b, p, and base point
113+
public EllipticCurve(BigInteger a, BigInteger b, BigInteger p, ECPoint basePoint) {
114+
this.a = a;
115+
this.b = b;
116+
this.p = p;
117+
this.basePoint = basePoint;
118+
}
119+
120+
// Constructor that randomly generates the curve parameters
121+
public EllipticCurve(int bits) {
122+
SecureRandom r = new SecureRandom();
123+
this.p = BigInteger.probablePrime(bits, r); // Random prime p
124+
this.a = new BigInteger(bits, r); // Random coefficient a
125+
this.b = new BigInteger(bits, r); // Random coefficient b
126+
this.basePoint = new ECPoint(BigInteger.valueOf(4), BigInteger.valueOf(8)); // Fixed base point G
127+
}
128+
129+
public ECPoint getBasePoint() {
130+
return basePoint;
131+
}
132+
133+
public BigInteger getP() {
134+
return p;
135+
}
136+
137+
public BigInteger getA() {
138+
return a;
139+
}
140+
141+
public BigInteger getB() {
142+
return b;
143+
}
144+
145+
public int getFieldSize() {
146+
return p.bitLength();
147+
}
148+
149+
public ECPoint encodeMessage(BigInteger message) {
150+
// Simple encoding of a message as an ECPoint (this is a simplified example)
151+
return new ECPoint(message, message);
152+
}
153+
154+
public BigInteger decodeMessage(ECPoint point) {
155+
return point.getX(); // Decode the message from ECPoint (simplified)
156+
}
157+
}
158+
159+
/**
160+
* Class representing a point on the elliptic curve.
161+
*/
162+
public static class ECPoint {
163+
private final BigInteger x; // X-coordinate of the point
164+
private final BigInteger y; // Y-coordinate of the point
165+
166+
public ECPoint(BigInteger x, BigInteger y) {
167+
this.x = x;
168+
this.y = y;
169+
}
170+
171+
public BigInteger getX() {
172+
return x;
173+
}
174+
175+
public BigInteger getY() {
176+
return y;
177+
}
178+
179+
@Override
180+
public String toString() {
181+
return "ECPoint(x=" + x.toString() + ", y=" + y.toString() + ")";
182+
}
183+
184+
/**
185+
* Add two points on the elliptic curve.
186+
*/
187+
public ECPoint add(ECPoint other, BigInteger p, BigInteger a) {
188+
if (this.x.equals(BigInteger.ZERO) && this.y.equals(BigInteger.ZERO)) {
189+
return other; // If this point is the identity, return the other point
190+
}
191+
if (other.x.equals(BigInteger.ZERO) && other.y.equals(BigInteger.ZERO)) {
192+
return this; // If the other point is the identity, return this point
193+
}
194+
195+
BigInteger lambda;
196+
if (this.equals(other)) {
197+
// Special case: point doubling
198+
lambda = (this.x.pow(2).multiply(BigInteger.valueOf(3)).add(a))
199+
.multiply(this.y.multiply(BigInteger.valueOf(2)).modInverse(p)).mod(p);
200+
} else {
201+
// General case: adding two different points
202+
lambda = (other.y.subtract(this.y))
203+
.multiply(other.x.subtract(this.x).modInverse(p)).mod(p);
204+
}
205+
206+
BigInteger xr = lambda.pow(2).subtract(this.x).subtract(other.x).mod(p);
207+
BigInteger yr = lambda.multiply(this.x.subtract(xr)).subtract(this.y).mod(p);
208+
209+
return new ECPoint(xr, yr);
210+
}
211+
212+
/**
213+
* Subtract two points on the elliptic curve.
214+
*/
215+
public ECPoint subtract(ECPoint other, BigInteger p, BigInteger a) {
216+
ECPoint negOther = new ECPoint(other.x, p.subtract(other.y)); // Negate the Y coordinate
217+
return this.add(negOther, p, a); // Add the negated point
218+
}
219+
220+
/**
221+
* Multiply a point by a scalar (repeated addition).
222+
*/
223+
public ECPoint multiply(BigInteger k, BigInteger p, BigInteger a) {
224+
ECPoint result = new ECPoint(BigInteger.ZERO, BigInteger.ZERO); // Identity point
225+
ECPoint addend = this;
226+
227+
while (k.signum() > 0) {
228+
if (k.testBit(0)) {
229+
result = result.add(addend, p, a); // Add when k's bit is 1
230+
}
231+
addend = addend.add(addend, p, a); // Double the point
232+
k = k.shiftRight(1); // Shift k to the right by 1 bit
233+
}
234+
235+
return result; // Return the resulting point
236+
}
237+
}
238+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package com.thealgorithms.ciphers;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertNotEquals;
5+
6+
import org.junit.jupiter.api.Test;
7+
import java.math.BigInteger;
8+
9+
/**
10+
* ECCTest - Unit tests for the ECC (Elliptic Curve Cryptography) implementation.
11+
* This class contains various test cases to validate the encryption and decryption functionalities.
12+
* It ensures the correctness and randomness of ECC operations.
13+
*
14+
* @author xuyang
15+
*/
16+
public class ECCTest {
17+
ECC ecc = new ECC(256); // Generate a 256-bit ECC key pair. Calls generateKeys(bits) to create keys including privateKey and publicKey.
18+
19+
/**
20+
* Test the encryption functionality: convert plaintext to ciphertext and output relevant encryption data.
21+
*/
22+
@Test
23+
void testEncrypt() {
24+
String textToEncrypt = "Elliptic Curve Cryptography";
25+
26+
ECC.ECPoint[] cipherText = ecc.encrypt(textToEncrypt); // Perform encryption
27+
28+
// Output private key information
29+
System.out.println("Private Key: " + ecc.getPrivateKey());
30+
31+
// Output elliptic curve parameters
32+
ECC.EllipticCurve curve = ecc.getCurve();
33+
System.out.println("Elliptic Curve Parameters:");
34+
System.out.println("a: " + curve.getA());
35+
System.out.println("b: " + curve.getB());
36+
System.out.println("p: " + curve.getP());
37+
System.out.println("Base Point G: " + curve.getBasePoint());
38+
39+
// Verify that the ciphertext is not empty
40+
assertEquals(cipherText.length, 2); // Check if the ciphertext contains two points (R and S)
41+
42+
// Output the encrypted coordinate points
43+
System.out.println("Encrypted Points:");
44+
for (ECC.ECPoint point : cipherText) {
45+
System.out.println(point); // Calls ECPoint's toString() method
46+
}
47+
}
48+
49+
/**
50+
* Test the decryption functionality: convert ciphertext back to plaintext using known private key and elliptic curve parameters.
51+
*/
52+
@Test
53+
void testDecryptWithKnownValues() {
54+
// 1. Define the known private key
55+
BigInteger knownPrivateKey = new BigInteger("28635978664199231399690075483195602260051035216440375973817268759912070302603");
56+
57+
// 2. Define the known elliptic curve parameters
58+
BigInteger a = new BigInteger("64505295837372135469230827475895976532873592609649950000895066186842236488761"); // Replace with known a value
59+
BigInteger b = new BigInteger("89111668838830965251111555638616364203833415376750835901427122343021749874324"); // Replace with known b value
60+
BigInteger p = new BigInteger("107276428198310591598877737561885175918069075479103276920057092968372930219921"); // Replace with known p value
61+
ECC.ECPoint basePoint = new ECC.ECPoint(new BigInteger("4"), new BigInteger("8")); // Replace with known base point coordinates
62+
63+
// 3. Create the elliptic curve object
64+
ECC.EllipticCurve curve = new ECC.EllipticCurve(a, b, p, basePoint);
65+
66+
// 4. Define the known ciphertext containing two ECPoints (R, S)
67+
ECC.ECPoint R = new ECC.ECPoint(new BigInteger("103077584019003058745849614420912636617007257617156724481937620119667345237687"),
68+
new BigInteger("68193862907937248121971710522760893811582068323088661566426323952783362061817"));
69+
ECC.ECPoint S = new ECC.ECPoint(new BigInteger("31932232426664380635434632300383525435115368414929679432313910646436992147798"),
70+
new BigInteger("77299754382292904069123203569944908076819220797512755280123348910207308129766"));
71+
ECC.ECPoint[] cipherText = new ECC.ECPoint[] { R, S };
72+
73+
// 5. Create an ECC instance and set the private key and curve parameters
74+
ecc.setPrivateKey(knownPrivateKey); // Use setter method to set the private key
75+
ecc.setCurve(curve); // Use setter method to set the elliptic curve
76+
77+
// 6. Decrypt the known ciphertext
78+
String decryptedMessage = ecc.decrypt(cipherText);
79+
80+
// 7. Compare the decrypted plaintext with the expected value
81+
String expectedMessage = "Elliptic Curve Cryptography"; // Expected plaintext
82+
assertEquals(expectedMessage, decryptedMessage);
83+
}
84+
85+
/**
86+
* Test that encrypting the same plaintext with ECC produces different ciphertexts.
87+
*/
88+
@Test
89+
void testCipherTextRandomness() {
90+
String message = "Elliptic Curve Cryptography";
91+
92+
ECC.ECPoint[] cipherText1 = ecc.encrypt(message);
93+
ECC.ECPoint[] cipherText2 = ecc.encrypt(message);
94+
95+
assertNotEquals(cipherText1, cipherText2); // Ensure that the two ciphertexts are different
96+
}
97+
98+
/**
99+
* Test the entire ECC encryption and decryption process.
100+
*/
101+
@Test
102+
void testECCEncryptionAndDecryption() {
103+
String textToEncrypt = "Elliptic Curve Cryptography";
104+
ECC.ECPoint[] cipherText = ecc.encrypt(textToEncrypt);
105+
String decryptedText = ecc.decrypt(cipherText);
106+
assertEquals(textToEncrypt, decryptedText); // Verify that the decrypted text matches the original text
107+
}
108+
}

0 commit comments

Comments
 (0)