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

127 statements  

1"""Implementation of Edwards Digital Signature Algorithm.""" 

2 

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) 

13 

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 

24 

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 

38 

39 

40def _sha512(data): 

41 return hashlib.new("sha512", compat26_str(data)).digest() 

42 

43 

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) 

48 

49 

50# edwards448, defined in RFC7748 

51_p = 2**448 - 2**224 - 1 

52_a = 1 

53_d = -39081 % _p 

54_h = 4 

55 

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 

71 

72 

73def _shake256(data): 

74 return shake_256(data, 114) 

75 

76 

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) 

81 

82 

83class PublicKey(object): 

84 """Public key for the Edwards Digital Signature Algorithm.""" 

85 

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 ) 

104 

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 

111 

112 def __ne__(self, other): 

113 return not self == other 

114 

115 @property 

116 def point(self): 

117 return self.__point 

118 

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 

124 

125 def public_point(self): 

126 return self.__point 

127 

128 def public_key(self): 

129 return self.__encoded 

130 

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") 

146 

147 dom = bytearray() 

148 if self.curve == curve_ed448: 

149 dom = bytearray(b"SigEd448" + b"\x00\x00") 

150 

151 k = bytes_to_int( 

152 self.curve.hash_func(dom + R.to_bytes() + self.__encoded + data), 

153 "little", 

154 ) 

155 

156 if self.generator * S != self.__point * k + R: 

157 raise ValueError("Invalid signature") 

158 

159 return True 

160 

161 

162class PrivateKey(object): 

163 """Private key for the Edwards Digital Signature Algorithm.""" 

164 

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 

179 

180 a = self.__h[: self.baselen] 

181 a = self._key_prune(a) 

182 scalar = bytes_to_int(a, "little") 

183 self.__s = scalar 

184 

185 @property 

186 def private_key(self): 

187 return self.__private_key 

188 

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 

196 

197 def __ne__(self, other): 

198 return not self == other 

199 

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) 

210 

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 

219 

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 

224 

225 public_point = self.generator * self.__s 

226 

227 self.__public_key = PublicKey( 

228 self.generator, public_point.to_bytes(), public_point 

229 ) 

230 

231 return self.__public_key 

232 

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() 

237 

238 prefix = self.__h[self.baselen :] 

239 

240 dom = bytearray() 

241 if self.curve == curve_ed448: 

242 dom = bytearray(b"SigEd448" + b"\x00\x00") 

243 

244 r = bytes_to_int(self.curve.hash_func(dom + prefix + data), "little") 

245 R = (self.generator * r).to_bytes() 

246 

247 k = bytes_to_int(self.curve.hash_func(dom + R + A + data), "little") 

248 k %= self.generator.order() 

249 

250 S = (r + k * self.__s) % self.generator.order() 

251 

252 return R + int_to_bytes(S, self.baselen, "little")