Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/ecdsa/ecdh.py: 40%

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

72 statements  

1""" 

2Class for performing Elliptic-curve Diffie-Hellman (ECDH) operations. 

3""" 

4 

5from .util import number_to_string 

6from .ellipticcurve import INFINITY 

7from .keys import SigningKey, VerifyingKey 

8 

9 

10__all__ = [ 

11 "ECDH", 

12 "NoKeyError", 

13 "NoCurveError", 

14 "InvalidCurveError", 

15 "InvalidSharedSecretError", 

16] 

17 

18 

19class NoKeyError(Exception): 

20 """ECDH. Key not found but it is needed for operation.""" 

21 

22 pass 

23 

24 

25class NoCurveError(Exception): 

26 """ECDH. Curve not set but it is needed for operation.""" 

27 

28 pass 

29 

30 

31class InvalidCurveError(Exception): 

32 """ 

33 ECDH. Raised in case the public and private keys use different curves. 

34 """ 

35 

36 pass 

37 

38 

39class InvalidSharedSecretError(Exception): 

40 """ECDH. Raised in case the shared secret we obtained is an INFINITY.""" 

41 

42 pass 

43 

44 

45class ECDH(object): 

46 """ 

47 Elliptic-curve Diffie-Hellman (ECDH). A key agreement protocol. 

48 

49 Allows two parties, each having an elliptic-curve public-private key 

50 pair, to establish a shared secret over an insecure channel 

51 """ 

52 

53 def __init__(self, curve=None, private_key=None, public_key=None): 

54 """ 

55 ECDH init. 

56 

57 Call can be initialised without parameters, then the first operation 

58 (loading either key) will set the used curve. 

59 All parameters must be ultimately set before shared secret 

60 calculation will be allowed. 

61 

62 :param curve: curve for operations 

63 :type curve: Curve 

64 :param private_key: `my` private key for ECDH 

65 :type private_key: SigningKey 

66 :param public_key: `their` public key for ECDH 

67 :type public_key: VerifyingKey 

68 """ 

69 self.curve = curve 

70 self.private_key = None 

71 self.public_key = None 

72 if private_key: 

73 self.load_private_key(private_key) 

74 if public_key: 

75 self.load_received_public_key(public_key) 

76 

77 def _get_shared_secret(self, remote_public_key): 

78 if not self.private_key: 

79 raise NoKeyError( 

80 "Private key needs to be set to create shared secret" 

81 ) 

82 if not self.public_key: 

83 raise NoKeyError( 

84 "Public key needs to be set to create shared secret" 

85 ) 

86 if not ( 

87 self.private_key.curve == self.curve == remote_public_key.curve 

88 ): 

89 raise InvalidCurveError( 

90 "Curves for public key and private key is not equal." 

91 ) 

92 

93 # shared secret = PUBKEYtheirs * PRIVATEKEYours 

94 result = ( 

95 remote_public_key.pubkey.point 

96 * self.private_key.privkey.secret_multiplier 

97 ) 

98 if result == INFINITY: 

99 raise InvalidSharedSecretError("Invalid shared secret (INFINITY).") 

100 

101 return result.x() 

102 

103 def set_curve(self, key_curve): 

104 """ 

105 Set the working curve for ecdh operations. 

106 

107 :param key_curve: curve from `curves` module 

108 :type key_curve: Curve 

109 """ 

110 self.curve = key_curve 

111 

112 def generate_private_key(self): 

113 """ 

114 Generate local private key for ecdh operation with curve that was set. 

115 

116 :raises NoCurveError: Curve must be set before key generation. 

117 

118 :return: public (verifying) key from this private key. 

119 :rtype: VerifyingKey 

120 """ 

121 if not self.curve: 

122 raise NoCurveError("Curve must be set prior to key generation.") 

123 return self.load_private_key(SigningKey.generate(curve=self.curve)) 

124 

125 def load_private_key(self, private_key): 

126 """ 

127 Load private key from SigningKey (keys.py) object. 

128 

129 Needs to have the same curve as was set with set_curve method. 

130 If curve is not set - it sets from this SigningKey 

131 

132 :param private_key: Initialised SigningKey class 

133 :type private_key: SigningKey 

134 

135 :raises InvalidCurveError: private_key curve not the same as self.curve 

136 

137 :return: public (verifying) key from this private key. 

138 :rtype: VerifyingKey 

139 """ 

140 if not self.curve: 

141 self.curve = private_key.curve 

142 if self.curve != private_key.curve: 

143 raise InvalidCurveError("Curve mismatch.") 

144 self.private_key = private_key 

145 return self.private_key.get_verifying_key() 

146 

147 def load_private_key_bytes(self, private_key): 

148 """ 

149 Load private key from byte string. 

150 

151 Uses current curve and checks if the provided key matches 

152 the curve of ECDH key agreement. 

153 Key loads via from_string method of SigningKey class 

154 

155 :param private_key: private key in bytes string format 

156 :type private_key: :term:`bytes-like object` 

157 

158 :raises NoCurveError: Curve must be set before loading. 

159 

160 :return: public (verifying) key from this private key. 

161 :rtype: VerifyingKey 

162 """ 

163 if not self.curve: 

164 raise NoCurveError("Curve must be set prior to key load.") 

165 return self.load_private_key( 

166 SigningKey.from_string(private_key, curve=self.curve) 

167 ) 

168 

169 def load_private_key_der(self, private_key_der): 

170 """ 

171 Load private key from DER byte string. 

172 

173 Compares the curve of the DER-encoded key with the ECDH set curve, 

174 uses the former if unset. 

175 

176 Note, the only DER format supported is the RFC5915 

177 Look at keys.py:SigningKey.from_der() 

178 

179 :param private_key_der: string with the DER encoding of private ECDSA 

180 key 

181 :type private_key_der: string 

182 

183 :raises InvalidCurveError: private_key curve not the same as self.curve 

184 

185 :return: public (verifying) key from this private key. 

186 :rtype: VerifyingKey 

187 """ 

188 return self.load_private_key(SigningKey.from_der(private_key_der)) 

189 

190 def load_private_key_pem(self, private_key_pem): 

191 """ 

192 Load private key from PEM string. 

193 

194 Compares the curve of the DER-encoded key with the ECDH set curve, 

195 uses the former if unset. 

196 

197 Note, the only PEM format supported is the RFC5915 

198 Look at keys.py:SigningKey.from_pem() 

199 it needs to have `EC PRIVATE KEY` section 

200 

201 :param private_key_pem: string with PEM-encoded private ECDSA key 

202 :type private_key_pem: string 

203 

204 :raises InvalidCurveError: private_key curve not the same as self.curve 

205 

206 :return: public (verifying) key from this private key. 

207 :rtype: VerifyingKey 

208 """ 

209 return self.load_private_key(SigningKey.from_pem(private_key_pem)) 

210 

211 def get_public_key(self): 

212 """ 

213 Provides a public key that matches the local private key. 

214 

215 Needs to be sent to the remote party. 

216 

217 :return: public (verifying) key from local private key. 

218 :rtype: VerifyingKey 

219 """ 

220 return self.private_key.get_verifying_key() 

221 

222 def load_received_public_key(self, public_key): 

223 """ 

224 Load public key from VerifyingKey (keys.py) object. 

225 

226 Needs to have the same curve as set as current for ecdh operation. 

227 If curve is not set - it sets it from VerifyingKey. 

228 

229 :param public_key: Initialised VerifyingKey class 

230 :type public_key: VerifyingKey 

231 

232 :raises InvalidCurveError: public_key curve not the same as self.curve 

233 """ 

234 if not self.curve: 

235 self.curve = public_key.curve 

236 if self.curve != public_key.curve: 

237 raise InvalidCurveError("Curve mismatch.") 

238 self.public_key = public_key 

239 

240 def load_received_public_key_bytes( 

241 self, public_key_str, valid_encodings=None 

242 ): 

243 """ 

244 Load public key from byte string. 

245 

246 Uses current curve and checks if key length corresponds to 

247 the current curve. 

248 Key loads via from_string method of VerifyingKey class 

249 

250 :param public_key_str: public key in bytes string format 

251 :type public_key_str: :term:`bytes-like object` 

252 :param valid_encodings: list of acceptable point encoding formats, 

253 supported ones are: :term:`uncompressed`, :term:`compressed`, 

254 :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` 

255 name). All formats by default (specified with ``None``). 

256 :type valid_encodings: :term:`set-like object` 

257 """ 

258 return self.load_received_public_key( 

259 VerifyingKey.from_string( 

260 public_key_str, self.curve, valid_encodings 

261 ) 

262 ) 

263 

264 def load_received_public_key_der(self, public_key_der): 

265 """ 

266 Load public key from DER byte string. 

267 

268 Compares the curve of the DER-encoded key with the ECDH set curve, 

269 uses the former if unset. 

270 

271 Note, the only DER format supported is the RFC5912 

272 Look at keys.py:VerifyingKey.from_der() 

273 

274 :param public_key_der: string with the DER encoding of public ECDSA key 

275 :type public_key_der: string 

276 

277 :raises InvalidCurveError: public_key curve not the same as self.curve 

278 """ 

279 return self.load_received_public_key( 

280 VerifyingKey.from_der(public_key_der) 

281 ) 

282 

283 def load_received_public_key_pem(self, public_key_pem): 

284 """ 

285 Load public key from PEM string. 

286 

287 Compares the curve of the PEM-encoded key with the ECDH set curve, 

288 uses the former if unset. 

289 

290 Note, the only PEM format supported is the RFC5912 

291 Look at keys.py:VerifyingKey.from_pem() 

292 

293 :param public_key_pem: string with PEM-encoded public ECDSA key 

294 :type public_key_pem: string 

295 

296 :raises InvalidCurveError: public_key curve not the same as self.curve 

297 """ 

298 return self.load_received_public_key( 

299 VerifyingKey.from_pem(public_key_pem) 

300 ) 

301 

302 def generate_sharedsecret_bytes(self): 

303 """ 

304 Generate shared secret from local private key and remote public key. 

305 

306 The objects needs to have both private key and received public key 

307 before generation is allowed. 

308 

309 :raises InvalidCurveError: public_key curve not the same as self.curve 

310 :raises NoKeyError: public_key or private_key is not set 

311 

312 :return: shared secret 

313 :rtype: bytes 

314 """ 

315 return number_to_string( 

316 self.generate_sharedsecret(), self.private_key.curve.curve.p() 

317 ) 

318 

319 def generate_sharedsecret(self): 

320 """ 

321 Generate shared secret from local private key and remote public key. 

322 

323 The objects needs to have both private key and received public key 

324 before generation is allowed. 

325 

326 It's the same for local and remote party, 

327 shared secret(local private key, remote public key) == 

328 shared secret(local public key, remote private key) 

329 

330 :raises InvalidCurveError: public_key curve not the same as self.curve 

331 :raises NoKeyError: public_key or private_key is not set 

332 

333 :return: shared secret 

334 :rtype: int 

335 """ 

336 return self._get_shared_secret(self.public_key)