Skip to content

ECC

ECC

Elliptic Curve Cryptography Math Introduction
Elliptic Curve Cryptography Math Introduction with examples
Elliptic Curve Cryptography with Graphs and code
The Animated Elliptic Curve

Security:
- Needs Good RNGs
- Bad Curves

Definitions:
- g is a generator point. Depending on the Curve this can be any point or a specific subset of points
- n is the order. This is how many Points exist in the generator point before repeating. Aka P*n = infinity point

Properties of a Elliptic Curve

Elliptic Curve Cryptography uses a collection of X,Y coordinates where x and y range is from [0,p].
This plain is symmetrical across the x axis

Because of this modulus of p where it is a prime number the x,y plane is associative and commutative.

Example Elliptic Curve Formula:

\[ y^2 = x^3 + ax + b \pmod{P} \]

Multiplying by a integer converts it into a trapdoor function.

Given a point P and a number n, computing a point Q such that Q = n * P is very easy.
Given a point P and a point Q, finding n such that Q = n * P is extremely hard.

Adding Mod

Like always we use a mod of a prime number to make sure that there is not a message that is divisible by the modulus.

When adding the mod a property shows its self. For some integer n there exists the congruency nP = 0. This also means that (n + 1)P = P

Addition on an Elliptic Curve

Source

Take an origin point P and a travel point Q. With a line going from point P to point Q there is either 1 point or no points. If the point exists than lets call that point R.

There exists a point -R which is just reflected over the X axis. This is also on the curve since the curve is symmetrical over the X axis.

Find Slope Example:

\[ P + Q = R (x_p, y_p) + (x_q, y_q) = (x_r, y_r) slope = \dfrac{y_q - y_p}{x_q - x_p} \]

Find Point on Curve:

\[ x_r = slope^2 - x_q - x_p y_r = -( y_q + slope * ( x_r - x_q)) \]

Point Addition:

	def add_point(self, point1, point2):
		#Check if both points are on the curve
		if (not self.is_on_curve(point1)) or (not self.is_on_curve(point2)):
			raise ValueError("The points are not on the curve.")

		#Check if either are infinity points
		if point1.isInifinityPoint():
			return point2
		elif point2.isInifinityPoint():
			return point1

		#Check for other relation properties
		if point1 == point2:
			#Double Point because needs specific slope calculation
			return self._double_point(point1)
		if point1 == -point2:
			#Return Infinity point
			return Point(None, None, self)

		#Do Curve specific Point Addition
		return self._add_point(point1, point2)

	def double_point(self, point):
		if not self.is_on_curve(point):
			raise ValueError("The point is not on the curve.")
		if point.isInifinityPoint():
			#Return Infinity point
			return Point(None, None, self)

		#Do Curve Specific Point Addition
		return self._double_point(point)



	def _add_point(self, point1, point2):
		# Compute the slope using y2-y1/x2-x1. Using a mod inverse instead of division
		slope = (point2.y - point1.y) * modinv(point2.x - point1.x, self.prime_mod)

		#Compute the new X point
		# y = mx + d
		# d = y_1 - m(x_1)

		# y^2 = x^3 + a*x + b
		# b = (y_1)^2 - (x_1)^3 + x(x_1)

		# y^2 = (mx + d)^2 
		#     = m^2x^2 + 2mxd + d^2 
		#     = m^2x^2 + 2mx(y_1 - m(x_1)) + (y_1 - m(x_1))^2
		#     = x^2(m^2) + 2x(m(y_1) - (m^2)(x_1)) + (y_1)^2 - 2m(x_1)(y_1) + m^2(x_1)^2

		# Set the functions equal to each other and solve for zero
		# x^2(m^2) + 2x(m(y_1) - (m^2)(x_1)) + (y_1)^2 - 2m(x_1)(y_1) + m^2(x_1)^2 = x^3 + a*x + b
		# x^2(m^2) + 2x(m(y_1) - (m^2)(x_1)) + (y_1)^2 - 2m(x_1)(y_1) + m^2(x_1)^2 -x^3 - a*x - b = 0
		# x^3 - x^2(m^2) - 2x(m(y_1) + (m^2)(x_1)) - (y_1)^2 + 2m(x_1)(y_1) - m^2(x_1)^2 + a*x + b = 0

		#We know the roots of the equation (x - x_1)(x - x_2)(x - x_3)
		#Lets set them equal to each other
		# x^3 - x^2(m^2) - 2x(m(y_1) + (m^2)(x_1)) - (y_1)^2 + 2m(x_1)(y_1) - m^2(x_1)^2 + a*x + b = (x - x_1)(x - x_2)(x - x_3)
		# x^3 - x^2(m^2) - 2x(m(y_1) + (m^2)(x_1)) - (y_1)^2 + 2m(x_1)(y_1) - m^2(x_1)^2 + a*x + b = x^3 -x^2((x_3) + (x_2) + (x_1)) + x((x_1)(x_2) + (x_1)(x_3) + (x_2)(x_3)) - (x_1)(x_2)(x_3)

		# That means that -x^2(m^2) = -x^2((x_3) + (x_2) + (x_1))
		# (m^2) = ((x_3) + (x_2) + (x_1))
		# x_3 = (m^2) - (x_2) - (x_1)

		# x = m^2 -x_1 -x_2
		result_x = (slope**2 - point1.x - point2.x) % self.prime_mod

		#Once we know x its easy to find y
		# y = mx + d
		# d = y_1 - m(x_1)
		# y = mx + y_1 - m(x_1)
		# y_3 = m(x_3) + y_1 - m(x_1)
		# y_3 = m((x_3) - (x_1)) + y_1

		#We take the negative since we want -R which is reflected over the x axis
		result_y = (-(slope * (result_x - point1.x) + point1.y)) % self.prime_mod
		return Point(point_x=result_x, point_y=result_y, curve=self)


	def _double_point(self, point1):
		#Find the tangent of the line at the point
		#This is done by taking the dirivitive of the Cure formula y^2 = x^3 + a*x + b (mod p)
		# 2y = 3x^2 + a 
		slope = (3 * point1.x**2 + self.a) * modinv(2 * point1.y, self.prime_mod)

		#Compute the new X point
		# y = mx + d
		# d = y_1 - m(x_1)

		# y^2 = x^3 + a*x + b
		# b = (y_1)^2 - (x_1)^3 + x(x_1)

		# y^2 = (mx + d)^2 
		#     = m^2x^2 + 2mxd + d^2 
		#     = m^2x^2 + 2mx(y_1 - m(x_1)) + (y_1 - m(x_1))^2
		#     = x^2(m^2) + 2x(m(y_1) - (m^2)(x_1)) + (y_1)^2 - 2m(x_1)(y_1) + m^2(x_1)^2

		# Set the functions equal to each other and solve for zero
		# x^2(m^2) + 2x(m(y_1) - (m^2)(x_1)) + (y_1)^2 - 2m(x_1)(y_1) + m^2(x_1)^2 = x^3 + a*x + b
		# x^2(m^2) + 2x(m(y_1) - (m^2)(x_1)) + (y_1)^2 - 2m(x_1)(y_1) + m^2(x_1)^2 -x^3 - a*x - b = 0
		# x^3 - x^2(m^2) - 2x(m(y_1) + (m^2)(x_1)) - (y_1)^2 + 2m(x_1)(y_1) - m^2(x_1)^2 + a*x + b = 0


		#We know the roots of the equation (x - x_1)(x - x_2)(x - x_3)
		#Lets set them equal to each other
		# x^3 - x^2(m^2) - 2x(m(y_1) + (m^2)(x_1)) - (y_1)^2 + 2m(x_1)(y_1) - m^2(x_1)^2 + a*x + b = (x - x_1)(x - x_1)(x - x_3)
		# x^3 - x^2(m^2) - 2x(m(y_1) + (m^2)(x_1)) - (y_1)^2 + 2m(x_1)(y_1) - m^2(x_1)^2 + a*x + b = + x^3 - x^2(2(x_1) + (x_3)) + x(x_1)^2 + 2x(x_1)(x_3)  -(x_1)^2(x_3)

		# That means that - x^2(m^2) = - x^2(2(x_1) + (x_3))
		# m^2 = 2(x_1) + (x_3)
		# m^2 - 2(x_1) = (x_3)

		# Compute the new point. This is the same info from the add point
		# Since there is no second point it is just 2* the same point
		result_x = (slope**2 - (2 * point1.x)) % self.prime_mod

		# y   = m*(x - x_1) + y_1
		# y_3 = m*(x_3 - x_1) + y_1

		#We take the negative since we want -R which is reflected over the x axis
		result_y = (-(slope * (result_x - point1.x) + point1.y)) % self.prime_mod
		return Point(point_x=result_x, point_y=result_y, curve=self)

Multiplications on an Elliptic Curve

Addition is just multiple additions. Similar to the Square and Multiply for bits in a RSA key the same thing can be done here.

#R = k * P

	def mul_point(self, scalar, point):
		#Check if Point is on Curve
		if not self.is_on_curve(point):
			raise ValueError("The point is not on the curve.")

		#Check if point Provided is Infinity
		if point.isInifinityPoint():
			#Return Infinity point
			return Point(None, None, self)

		#Check if multiplied by Zero
		if scalar == 0:
			#Return Infinity point
			return Point(None, None, self)

		#Check if Scalar is negative
		if scalar < 0:
			# Split the negative Multiplication into -1 * scalar
			# This allows the regular operations then taking the negative of the resulting point
			temp_scalar = -scalar
		else:
			temp_scalar = scalar


		#Initialize result to the Infinity point
		result = Point(None, None, self)
		temp_point = point

		while temp_scalar:
			#Check if current least significant bit is set
			# If set then add the initial point to the running total
			if temp_scalar & 0x1 == 1:
				result = temp_point + result

			#Increase the Point by 2 to be used if the bit is set
			temp_point = self.double_point(temp_point)

			#Decrease the scalor by 2 to check the next least significant
			temp_scalar >>= 1

		#Check if the scalar was negitive and invert the result
		if scalar < 0:
			return -result
		else:
			return result

Order of Elliptic Curve

The Order of a Curve is the max number of valid points on the curve. This includes the infinity point.
- This is calculated by finding the size of all of the subgroups.

Subgroups are non-overlaping sets of possible results that a point can be.
- A sub-group could be all of the possible results that n * point1 could be.

Calculating and Order with Sage Math:

from sage.all import *
E = EllipticCurve(GF(310717010502520989590157367261876774703), [2, 3])
generator_point = E(179210853392303317793440285562762725654, 105268671499942631758568591033409611165)
print(f"Order: {E.order()}") # 310717010502520989590206149059164677804

#Check to see if the order is easily factorable
order_factors = factor(E.order())
print(order_factors) # 2^2 * 3^7 * 139 * 165229 * 31850531 * 270778799 * 179317983307

Cofactor of an Elliptic Curve

Cofactors are the number of subgroups that a curve contains. This is the factors of the order of the curve

Generator point

Generating the Generator Point:

from sage.all import *

def generator_point(E):
	order_coprime = [p for p, _ in E.order().factor()]
	for test_x in range(1, 100):

		test_points = E.lift_x(Integer(test_x), all=True)

		#Check if it has two points
		if len(test_points) == 2:
			pointOrder = test_points[0].order()

			print(test_points, pointOrder)

			#Check if order matches constraints
			# Is a prime number and is equal to the greatest prime from the curve order
			# aka in the large prime-order sub-group
			if pointOrder > 8 and pointOrder.is_prime() and pointOrder == order_coprime[-1]:
				return test_x, test_points[0], pointOrder.factor()
	

E = EllipticCurve(GF(pow(2,255) - 19), [0, 486662, 0, 1, 0])
print(f"Curve Order: {E.order()}")
print(f"Order Factors: {E.order().factor()}")
#Curve Order: 57896044618658097711785492504343953926856930875039260848015607506283634007912
#Order Factors: 2^3 * 7237005577332262213973186563042994240857116359379907606001950938285454250989

g = generator_point(E)
print(g)
"""
[(1 : 9094040566125962849133224048217411091405536248825867518642941381412595940312 : 1), (1 : 48802004052532134862652268456126542835229456083994414501085850622543968879637 : 1)] 4
[(4 : 10396089888167458996693606908380331970145732977558722329349539962582616845133 : 1), (4 : 47499954730490638715091885595963621956489259355261559690379252041373947974816 : 1)] 28948022309329048855892746252171976963428465437519630424007803753141817003956
[(6 : 24305686323100213963426648412256631801221636864960734090508095036257779400554 : 1), (6 : 33590358295557883748358844092087322125413355467859547929220696967698785419395 : 1)] 57896044618658097711785492504343953926856930875039260848015607506283634007912
[(7 : 22162570367908009971983332230403424946970232071349602096662526808647397026575 : 1), (7 : 35733474250750087739802160273940528979664760261470679923066265195309167793374 : 1)] 57896044618658097711785492504343953926856930875039260848015607506283634007912
[(8 : 20738790057349198289249091238452691228459490893327641026453294524045724516251 : 1), (8 : 37157254561308899422536401265891262698175501439492640993275497479910840303698 : 1)] 57896044618658097711785492504343953926856930875039260848015607506283634007912
[(9 : 14781619447589544791020593568409986887264606134616475288964881837755586237401 : 1), (9 : 43114425171068552920764898935933967039370386198203806730763910166200978582548 : 1)] 7237005577332262213973186563042994240857116359379907606001950938285454250989
(9, (9 : 14781619447589544791020593568409986887264606134616475288964881837755586237401 : 1), 7237005577332262213973186563042994240857116359379907606001950938285454250989)
"""

Example

secp256k1 Curve

Invalid Curve Point Attack

https://iacr.org/archive/pkc2003/25670211/25670211.pdf
https://blog.trailofbits.com/2018/08/01/bluetooth-invalid-curve-points/

Since a user chooses a point which contains both an x and y value the attacker can use any value for this.

Point on the Curve Checklist:
- The x and y values are smaller than the modulus
- The x and y values are using the curve formula are valid
- The Point is not an Infinity point
- The Point is in the correct subgroup

Values bigger than the Modulus

These values may mess with the Code. For some program languages like C there might be a maximum value for the integer. Going over that value might create invalid results

Example:

#24 * G = (115090238283566018960826468250608273126387416636633736439689841211757211870926, 47185183227829754668635270747409548752084785367264057948864458978444304762303)
	G24 = Point(curve=secp256k1, point_x=115090238283566018960826468250608273126387416636633736439689841211757211870926 + 10*secp256k1.prime_mod, point_y=47185183227829754668635270747409548752084785367264057948864458978444304762303+ 10000*secp256k1.prime_mod)
	print(secp256k1.is_on_curve(G24))
	#True
	print(G24)
	#secp256k1: 
	#x=1273011130656727973196536318337487351659087263293039376834265681290845558587556, 
	#y=1157968077556389783990378485357626488081451931441772904452524704538066791021392303

Point is not on Curve

If a point is from an easier curve to attack and the point is not checked. This might lead to leaking the secret Key or making the encrypted data able to be decrypted without a private key.

Example:

	#### Point is not on Curve
	invalid_point = Point(curve=secp256k1, point_x=secp256k1_Generator_Point.x, point_y=G24.y)
	print(secp256k1.is_on_curve(invalid_point))
	#False
	print(invalid_point)
	#secp256k1: x=55066263022277343669578718895168534326250603453777594175500187360389116729240, y=1157968077556389783990378485357626488081451931441772904452524704538066791021392303

Point is not an Infinity Point

If an infinity point is used then the

https://vnhacker.blogspot.com/2015/09/why-not-validating-curve25519-public.html