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
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"""
2Class for performing Elliptic-curve Diffie-Hellman (ECDH) operations.
3"""
5from .util import number_to_string
6from .ellipticcurve import INFINITY
7from .keys import SigningKey, VerifyingKey
10__all__ = [
11 "ECDH",
12 "NoKeyError",
13 "NoCurveError",
14 "InvalidCurveError",
15 "InvalidSharedSecretError",
16]
19class NoKeyError(Exception):
20 """ECDH. Key not found but it is needed for operation."""
22 pass
25class NoCurveError(Exception):
26 """ECDH. Curve not set but it is needed for operation."""
28 pass
31class InvalidCurveError(Exception):
32 """
33 ECDH. Raised in case the public and private keys use different curves.
34 """
36 pass
39class InvalidSharedSecretError(Exception):
40 """ECDH. Raised in case the shared secret we obtained is an INFINITY."""
42 pass
45class ECDH(object):
46 """
47 Elliptic-curve Diffie-Hellman (ECDH). A key agreement protocol.
49 Allows two parties, each having an elliptic-curve public-private key
50 pair, to establish a shared secret over an insecure channel
51 """
53 def __init__(self, curve=None, private_key=None, public_key=None):
54 """
55 ECDH init.
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.
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)
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 )
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).")
101 return result.x()
103 def set_curve(self, key_curve):
104 """
105 Set the working curve for ecdh operations.
107 :param key_curve: curve from `curves` module
108 :type key_curve: Curve
109 """
110 self.curve = key_curve
112 def generate_private_key(self):
113 """
114 Generate local private key for ecdh operation with curve that was set.
116 :raises NoCurveError: Curve must be set before key generation.
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))
125 def load_private_key(self, private_key):
126 """
127 Load private key from SigningKey (keys.py) object.
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
132 :param private_key: Initialised SigningKey class
133 :type private_key: SigningKey
135 :raises InvalidCurveError: private_key curve not the same as self.curve
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()
147 def load_private_key_bytes(self, private_key):
148 """
149 Load private key from byte string.
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
155 :param private_key: private key in bytes string format
156 :type private_key: :term:`bytes-like object`
158 :raises NoCurveError: Curve must be set before loading.
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 )
169 def load_private_key_der(self, private_key_der):
170 """
171 Load private key from DER byte string.
173 Compares the curve of the DER-encoded key with the ECDH set curve,
174 uses the former if unset.
176 Note, the only DER format supported is the RFC5915
177 Look at keys.py:SigningKey.from_der()
179 :param private_key_der: string with the DER encoding of private ECDSA
180 key
181 :type private_key_der: string
183 :raises InvalidCurveError: private_key curve not the same as self.curve
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))
190 def load_private_key_pem(self, private_key_pem):
191 """
192 Load private key from PEM string.
194 Compares the curve of the DER-encoded key with the ECDH set curve,
195 uses the former if unset.
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
201 :param private_key_pem: string with PEM-encoded private ECDSA key
202 :type private_key_pem: string
204 :raises InvalidCurveError: private_key curve not the same as self.curve
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))
211 def get_public_key(self):
212 """
213 Provides a public key that matches the local private key.
215 Needs to be sent to the remote party.
217 :return: public (verifying) key from local private key.
218 :rtype: VerifyingKey
219 """
220 return self.private_key.get_verifying_key()
222 def load_received_public_key(self, public_key):
223 """
224 Load public key from VerifyingKey (keys.py) object.
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.
229 :param public_key: Initialised VerifyingKey class
230 :type public_key: VerifyingKey
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
240 def load_received_public_key_bytes(
241 self, public_key_str, valid_encodings=None
242 ):
243 """
244 Load public key from byte string.
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
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 )
264 def load_received_public_key_der(self, public_key_der):
265 """
266 Load public key from DER byte string.
268 Compares the curve of the DER-encoded key with the ECDH set curve,
269 uses the former if unset.
271 Note, the only DER format supported is the RFC5912
272 Look at keys.py:VerifyingKey.from_der()
274 :param public_key_der: string with the DER encoding of public ECDSA key
275 :type public_key_der: string
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 )
283 def load_received_public_key_pem(self, public_key_pem):
284 """
285 Load public key from PEM string.
287 Compares the curve of the PEM-encoded key with the ECDH set curve,
288 uses the former if unset.
290 Note, the only PEM format supported is the RFC5912
291 Look at keys.py:VerifyingKey.from_pem()
293 :param public_key_pem: string with PEM-encoded public ECDSA key
294 :type public_key_pem: string
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 )
302 def generate_sharedsecret_bytes(self):
303 """
304 Generate shared secret from local private key and remote public key.
306 The objects needs to have both private key and received public key
307 before generation is allowed.
309 :raises InvalidCurveError: public_key curve not the same as self.curve
310 :raises NoKeyError: public_key or private_key is not set
312 :return: shared secret
313 :rtype: bytes
314 """
315 return number_to_string(
316 self.generate_sharedsecret(), self.private_key.curve.curve.p()
317 )
319 def generate_sharedsecret(self):
320 """
321 Generate shared secret from local private key and remote public key.
323 The objects needs to have both private key and received public key
324 before generation is allowed.
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)
330 :raises InvalidCurveError: public_key curve not the same as self.curve
331 :raises NoKeyError: public_key or private_key is not set
333 :return: shared secret
334 :rtype: int
335 """
336 return self._get_shared_secret(self.public_key)