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:
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¶
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:
Find Point on Curve:
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