package com.thealgorithms.ciphers; import java.math.BigInteger; import java.security.SecureRandom; /** * ECC - Elliptic Curve Cryptography * Elliptic Curve Cryptography is a public-key cryptography method that uses the algebraic structure of * elliptic curves over finite fields. ECC provides a higher level of security with smaller key sizes compared * to other public-key methods like RSA, making it particularly suitable for environments where computational * resources are limited, such as mobile devices and embedded systems. * * This class implements elliptic curve cryptography, providing encryption and decryption * functionalities based on public and private key pairs. * * @author xuyang */ public class ECC { private BigInteger privateKey; // Private key used for decryption private ECPoint publicKey; // Public key used for encryption private EllipticCurve curve; // Elliptic curve used in cryptography private ECPoint basePoint; // Base point G on the elliptic curve public ECC(int bits) { generateKeys(bits); // Generates public-private key pair } public EllipticCurve getCurve() { return curve; // Returns the elliptic curve } public void setCurve(EllipticCurve curve) { this.curve = curve; } // Getter and Setter for private key public BigInteger getPrivateKey() { return privateKey; } public void setPrivateKey(BigInteger privateKey) { this.privateKey = privateKey; } /** * Encrypts the message using the public key. * The message is transformed into an ECPoint and encrypted with elliptic curve operations. * * @param message The plain message to be encrypted * @return The encrypted message as an array of ECPoints (R, S) */ public ECPoint[] encrypt(String message) { BigInteger m = new BigInteger(message.getBytes()); // Convert message to BigInteger SecureRandom r = new SecureRandom(); // Generate random value for k BigInteger k = new BigInteger(curve.getFieldSize(), r); // Generate random scalar k // Calculate point r = k * G, where G is the base point ECPoint rPoint = basePoint.multiply(k, curve.getP(), curve.getA()); // Calculate point s = k * publicKey + encodedMessage ECPoint sPoint = publicKey.multiply(k, curve.getP(), curve.getA()).add(curve.encodeMessage(m), curve.getP(), curve.getA()); return new ECPoint[] {rPoint, sPoint}; // Return encrypted message as two ECPoints } /** * Decrypts the encrypted message using the private key. * The decryption process is the reverse of encryption, recovering the original message. * * @param encryptedMessage The encrypted message as an array of ECPoints (R, S) * @return The decrypted plain message as a String */ public String decrypt(ECPoint[] encryptedMessage) { ECPoint rPoint = encryptedMessage[0]; // First part of ciphertext ECPoint sPoint = encryptedMessage[1]; // Second part of ciphertext // Perform decryption: s - r * privateKey ECPoint decodedMessage = sPoint.subtract(rPoint.multiply(privateKey, curve.getP(), curve.getA()), curve.getP(), curve.getA()); BigInteger m = curve.decodeMessage(decodedMessage); // Decode the message from ECPoint return new String(m.toByteArray()); // Convert BigInteger back to String } /** * Generates a new public-private key pair for encryption and decryption. * * @param bits The size (in bits) of the keys to generate */ public final void generateKeys(int bits) { SecureRandom r = new SecureRandom(); curve = new EllipticCurve(bits); // Initialize a new elliptic curve basePoint = curve.getBasePoint(); // Set the base point G // Generate private key as a random BigInteger privateKey = new BigInteger(bits, r); // Generate public key as the point publicKey = privateKey * G publicKey = basePoint.multiply(privateKey, curve.getP(), curve.getA()); } /** * Class representing an elliptic curve with the form y^2 = x^3 + ax + b. */ public static class EllipticCurve { private final BigInteger a; // Coefficient a in the curve equation private final BigInteger b; // Coefficient b in the curve equation private final BigInteger p; // Prime number p, defining the finite field private final ECPoint basePoint; // Base point G on the curve // Constructor with explicit parameters for a, b, p, and base point public EllipticCurve(BigInteger a, BigInteger b, BigInteger p, ECPoint basePoint) { this.a = a; this.b = b; this.p = p; this.basePoint = basePoint; } // Constructor that randomly generates the curve parameters public EllipticCurve(int bits) { SecureRandom r = new SecureRandom(); this.p = BigInteger.probablePrime(bits, r); // Random prime p this.a = new BigInteger(bits, r); // Random coefficient a this.b = new BigInteger(bits, r); // Random coefficient b this.basePoint = new ECPoint(BigInteger.valueOf(4), BigInteger.valueOf(8)); // Fixed base point G } public ECPoint getBasePoint() { return basePoint; } public BigInteger getP() { return p; } public BigInteger getA() { return a; } public BigInteger getB() { return b; } public int getFieldSize() { return p.bitLength(); } public ECPoint encodeMessage(BigInteger message) { // Simple encoding of a message as an ECPoint (this is a simplified example) return new ECPoint(message, message); } public BigInteger decodeMessage(ECPoint point) { return point.getX(); // Decode the message from ECPoint (simplified) } } /** * Class representing a point on the elliptic curve. */ public static class ECPoint { private final BigInteger x; // X-coordinate of the point private final BigInteger y; // Y-coordinate of the point public ECPoint(BigInteger x, BigInteger y) { this.x = x; this.y = y; } public BigInteger getX() { return x; } public BigInteger getY() { return y; } @Override public String toString() { return "ECPoint(x=" + x.toString() + ", y=" + y.toString() + ")"; } /** * Add two points on the elliptic curve. */ public ECPoint add(ECPoint other, BigInteger p, BigInteger a) { if (this.x.equals(BigInteger.ZERO) && this.y.equals(BigInteger.ZERO)) { return other; // If this point is the identity, return the other point } if (other.x.equals(BigInteger.ZERO) && other.y.equals(BigInteger.ZERO)) { return this; // If the other point is the identity, return this point } BigInteger lambda; if (this.equals(other)) { // Special case: point doubling lambda = this.x.pow(2).multiply(BigInteger.valueOf(3)).add(a).multiply(this.y.multiply(BigInteger.valueOf(2)).modInverse(p)).mod(p); } else { // General case: adding two different points lambda = other.y.subtract(this.y).multiply(other.x.subtract(this.x).modInverse(p)).mod(p); } BigInteger xr = lambda.pow(2).subtract(this.x).subtract(other.x).mod(p); BigInteger yr = lambda.multiply(this.x.subtract(xr)).subtract(this.y).mod(p); return new ECPoint(xr, yr); } /** * Subtract two points on the elliptic curve. */ public ECPoint subtract(ECPoint other, BigInteger p, BigInteger a) { ECPoint negOther = new ECPoint(other.x, p.subtract(other.y)); // Negate the Y coordinate return this.add(negOther, p, a); // Add the negated point } /** * Multiply a point by a scalar (repeated addition). */ public ECPoint multiply(BigInteger k, BigInteger p, BigInteger a) { ECPoint result = new ECPoint(BigInteger.ZERO, BigInteger.ZERO); // Identity point ECPoint addend = this; while (k.signum() > 0) { if (k.testBit(0)) { result = result.add(addend, p, a); // Add the current point } addend = addend.add(addend, p, a); // Double the point k = k.shiftRight(1); // Divide k by 2 } return result; } } }