Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/ecdsa/eddsa.py: 53%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Implementation of Edwards Digital Signature Algorithm."""
3import hashlib
4from ._sha3 import shake_256
5from . import ellipticcurve
6from ._compat import (
7 remove_whitespace,
8 bit_length,
9 bytes_to_int,
10 int_to_bytes,
11 compat26_str,
12)
14# edwards25519, defined in RFC7748
15_p = 2**255 - 19
16_a = -1
17_d = int(
18 remove_whitespace(
19 "370957059346694393431380835087545651895421138798432190163887855330"
20 "85940283555"
21 )
22)
23_h = 8
25_Gx = int(
26 remove_whitespace(
27 "151122213495354007725011514095885315114540126930418572060461132"
28 "83949847762202"
29 )
30)
31_Gy = int(
32 remove_whitespace(
33 "463168356949264781694283940034751631413079938662562256157830336"
34 "03165251855960"
35 )
36)
37_r = 2**252 + 0x14DEF9DEA2F79CD65812631A5CF5D3ED
40def _sha512(data):
41 return hashlib.new("sha512", compat26_str(data)).digest()
44curve_ed25519 = ellipticcurve.CurveEdTw(_p, _a, _d, _h, _sha512)
45generator_ed25519 = ellipticcurve.PointEdwards(
46 curve_ed25519, _Gx, _Gy, 1, _Gx * _Gy % _p, _r, generator=True
47)
50# edwards448, defined in RFC7748
51_p = 2**448 - 2**224 - 1
52_a = 1
53_d = -39081 % _p
54_h = 4
56_Gx = int(
57 remove_whitespace(
58 "224580040295924300187604334099896036246789641632564134246125461"
59 "686950415467406032909029192869357953282578032075146446173674602635"
60 "247710"
61 )
62)
63_Gy = int(
64 remove_whitespace(
65 "298819210078481492676017930443930673437544040154080242095928241"
66 "372331506189835876003536878655418784733982303233503462500531545062"
67 "832660"
68 )
69)
70_r = 2**446 - 0x8335DC163BB124B65129C96FDE933D8D723A70AADC873D6D54A7BB0D
73def _shake256(data):
74 return shake_256(data, 114)
77curve_ed448 = ellipticcurve.CurveEdTw(_p, _a, _d, _h, _shake256)
78generator_ed448 = ellipticcurve.PointEdwards(
79 curve_ed448, _Gx, _Gy, 1, _Gx * _Gy % _p, _r, generator=True
80)
83class PublicKey(object):
84 """Public key for the Edwards Digital Signature Algorithm."""
86 def __init__(self, generator, public_key, public_point=None):
87 self.generator = generator
88 self.curve = generator.curve()
89 self.__encoded = public_key
90 # plus one for the sign bit and round up
91 self.baselen = (bit_length(self.curve.p()) + 1 + 7) // 8
92 if len(public_key) != self.baselen:
93 raise ValueError(
94 "Incorrect size of the public key, expected: {0} bytes".format(
95 self.baselen
96 )
97 )
98 if public_point:
99 self.__point = public_point
100 else:
101 self.__point = ellipticcurve.PointEdwards.from_bytes(
102 self.curve, public_key
103 )
105 def __eq__(self, other):
106 if isinstance(other, PublicKey):
107 return (
108 self.curve == other.curve and self.__encoded == other.__encoded
109 )
110 return NotImplemented
112 def __ne__(self, other):
113 return not self == other
115 @property
116 def point(self):
117 return self.__point
119 @point.setter
120 def point(self, other):
121 if self.__point != other:
122 raise ValueError("Can't change the coordinates of the point")
123 self.__point = other
125 def public_point(self):
126 return self.__point
128 def public_key(self):
129 return self.__encoded
131 def verify(self, data, signature):
132 """Verify a Pure EdDSA signature over data."""
133 data = compat26_str(data)
134 if len(signature) != 2 * self.baselen:
135 raise ValueError(
136 "Invalid signature length, expected: {0} bytes".format(
137 2 * self.baselen
138 )
139 )
140 R = ellipticcurve.PointEdwards.from_bytes(
141 self.curve, signature[: self.baselen]
142 )
143 S = bytes_to_int(signature[self.baselen :], "little")
144 if S >= self.generator.order():
145 raise ValueError("Invalid signature")
147 dom = bytearray()
148 if self.curve == curve_ed448:
149 dom = bytearray(b"SigEd448" + b"\x00\x00")
151 k = bytes_to_int(
152 self.curve.hash_func(dom + R.to_bytes() + self.__encoded + data),
153 "little",
154 )
156 if self.generator * S != self.__point * k + R:
157 raise ValueError("Invalid signature")
159 return True
162class PrivateKey(object):
163 """Private key for the Edwards Digital Signature Algorithm."""
165 def __init__(self, generator, private_key):
166 self.generator = generator
167 self.curve = generator.curve()
168 # plus one for the sign bit and round up
169 self.baselen = (bit_length(self.curve.p()) + 1 + 7) // 8
170 if len(private_key) != self.baselen:
171 raise ValueError(
172 "Incorrect size of private key, expected: {0} bytes".format(
173 self.baselen
174 )
175 )
176 self.__private_key = bytes(private_key)
177 self.__h = bytearray(self.curve.hash_func(private_key))
178 self.__public_key = None
180 a = self.__h[: self.baselen]
181 a = self._key_prune(a)
182 scalar = bytes_to_int(a, "little")
183 self.__s = scalar
185 @property
186 def private_key(self):
187 return self.__private_key
189 def __eq__(self, other):
190 if isinstance(other, PrivateKey):
191 return (
192 self.curve == other.curve
193 and self.__private_key == other.__private_key
194 )
195 return NotImplemented
197 def __ne__(self, other):
198 return not self == other
200 def _key_prune(self, key):
201 # make sure the key is not in a small subgroup
202 h = self.curve.cofactor()
203 if h == 4:
204 h_log = 2
205 elif h == 8:
206 h_log = 3
207 else:
208 raise ValueError("Only cofactor 4 and 8 curves supported")
209 key[0] &= ~((1 << h_log) - 1)
211 # ensure the highest bit is set but no higher
212 l = bit_length(self.curve.p())
213 if l % 8 == 0:
214 key[-1] = 0
215 key[-2] |= 0x80
216 else:
217 key[-1] = key[-1] & (1 << (l % 8)) - 1 | 1 << (l % 8) - 1
218 return key
220 def public_key(self):
221 """Generate the public key based on the included private key"""
222 if self.__public_key:
223 return self.__public_key
225 public_point = self.generator * self.__s
227 self.__public_key = PublicKey(
228 self.generator, public_point.to_bytes(), public_point
229 )
231 return self.__public_key
233 def sign(self, data):
234 """Perform a Pure EdDSA signature over data."""
235 data = compat26_str(data)
236 A = self.public_key().public_key()
238 prefix = self.__h[self.baselen :]
240 dom = bytearray()
241 if self.curve == curve_ed448:
242 dom = bytearray(b"SigEd448" + b"\x00\x00")
244 r = bytes_to_int(self.curve.hash_func(dom + prefix + data), "little")
245 R = (self.generator * r).to_bytes()
247 k = bytes_to_int(self.curve.hash_func(dom + R + A + data), "little")
248 k %= self.generator.order()
250 S = (r + k * self.__s) % self.generator.order()
252 return R + int_to_bytes(S, self.baselen, "little")