Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/cryptography/hazmat/backends/openssl/ec.py: 24%
144 statements
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-25 06:11 +0000
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-25 06:11 +0000
1# This file is dual licensed under the terms of the Apache License, Version
2# 2.0, and the BSD License. See the LICENSE file in the root of this repository
3# for complete details.
5import typing
7from cryptography.exceptions import (
8 InvalidSignature,
9 UnsupportedAlgorithm,
10 _Reasons,
11)
12from cryptography.hazmat.backends.openssl.utils import (
13 _calculate_digest_and_algorithm,
14 _evp_pkey_derive,
15)
16from cryptography.hazmat.primitives import serialization
17from cryptography.hazmat.primitives.asymmetric import ec
19if typing.TYPE_CHECKING:
20 from cryptography.hazmat.backends.openssl.backend import Backend
23def _check_signature_algorithm(
24 signature_algorithm: ec.EllipticCurveSignatureAlgorithm,
25) -> None:
26 if not isinstance(signature_algorithm, ec.ECDSA):
27 raise UnsupportedAlgorithm(
28 "Unsupported elliptic curve signature algorithm.",
29 _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
30 )
33def _ec_key_curve_sn(backend: "Backend", ec_key) -> str:
34 group = backend._lib.EC_KEY_get0_group(ec_key)
35 backend.openssl_assert(group != backend._ffi.NULL)
37 nid = backend._lib.EC_GROUP_get_curve_name(group)
38 # The following check is to find EC keys with unnamed curves and raise
39 # an error for now.
40 if nid == backend._lib.NID_undef:
41 raise ValueError(
42 "ECDSA keys with explicit parameters are unsupported at this time"
43 )
45 # This is like the above check, but it also catches the case where you
46 # explicitly encoded a curve with the same parameters as a named curve.
47 # Don't do that.
48 if (
49 not backend._lib.CRYPTOGRAPHY_IS_LIBRESSL
50 and backend._lib.EC_GROUP_get_asn1_flag(group) == 0
51 ):
52 raise ValueError(
53 "ECDSA keys with explicit parameters are unsupported at this time"
54 )
56 curve_name = backend._lib.OBJ_nid2sn(nid)
57 backend.openssl_assert(curve_name != backend._ffi.NULL)
59 sn = backend._ffi.string(curve_name).decode("ascii")
60 return sn
63def _mark_asn1_named_ec_curve(backend: "Backend", ec_cdata):
64 """
65 Set the named curve flag on the EC_KEY. This causes OpenSSL to
66 serialize EC keys along with their curve OID which makes
67 deserialization easier.
68 """
70 backend._lib.EC_KEY_set_asn1_flag(
71 ec_cdata, backend._lib.OPENSSL_EC_NAMED_CURVE
72 )
75def _check_key_infinity(backend: "Backend", ec_cdata) -> None:
76 point = backend._lib.EC_KEY_get0_public_key(ec_cdata)
77 backend.openssl_assert(point != backend._ffi.NULL)
78 group = backend._lib.EC_KEY_get0_group(ec_cdata)
79 backend.openssl_assert(group != backend._ffi.NULL)
80 if backend._lib.EC_POINT_is_at_infinity(group, point):
81 raise ValueError(
82 "Cannot load an EC public key where the point is at infinity"
83 )
86def _sn_to_elliptic_curve(backend: "Backend", sn: str) -> ec.EllipticCurve:
87 try:
88 return ec._CURVE_TYPES[sn]()
89 except KeyError:
90 raise UnsupportedAlgorithm(
91 "{} is not a supported elliptic curve".format(sn),
92 _Reasons.UNSUPPORTED_ELLIPTIC_CURVE,
93 )
96def _ecdsa_sig_sign(
97 backend: "Backend", private_key: "_EllipticCurvePrivateKey", data: bytes
98) -> bytes:
99 max_size = backend._lib.ECDSA_size(private_key._ec_key)
100 backend.openssl_assert(max_size > 0)
102 sigbuf = backend._ffi.new("unsigned char[]", max_size)
103 siglen_ptr = backend._ffi.new("unsigned int[]", 1)
104 res = backend._lib.ECDSA_sign(
105 0, data, len(data), sigbuf, siglen_ptr, private_key._ec_key
106 )
107 backend.openssl_assert(res == 1)
108 return backend._ffi.buffer(sigbuf)[: siglen_ptr[0]]
111def _ecdsa_sig_verify(
112 backend: "Backend",
113 public_key: "_EllipticCurvePublicKey",
114 signature: bytes,
115 data: bytes,
116) -> None:
117 res = backend._lib.ECDSA_verify(
118 0, data, len(data), signature, len(signature), public_key._ec_key
119 )
120 if res != 1:
121 backend._consume_errors()
122 raise InvalidSignature
125class _EllipticCurvePrivateKey(ec.EllipticCurvePrivateKey):
126 def __init__(self, backend: "Backend", ec_key_cdata, evp_pkey):
127 self._backend = backend
128 self._ec_key = ec_key_cdata
129 self._evp_pkey = evp_pkey
131 sn = _ec_key_curve_sn(backend, ec_key_cdata)
132 self._curve = _sn_to_elliptic_curve(backend, sn)
133 _mark_asn1_named_ec_curve(backend, ec_key_cdata)
134 _check_key_infinity(backend, ec_key_cdata)
136 @property
137 def curve(self) -> ec.EllipticCurve:
138 return self._curve
140 @property
141 def key_size(self) -> int:
142 return self.curve.key_size
144 def exchange(
145 self, algorithm: ec.ECDH, peer_public_key: ec.EllipticCurvePublicKey
146 ) -> bytes:
147 if not (
148 self._backend.elliptic_curve_exchange_algorithm_supported(
149 algorithm, self.curve
150 )
151 ):
152 raise UnsupportedAlgorithm(
153 "This backend does not support the ECDH algorithm.",
154 _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
155 )
157 if peer_public_key.curve.name != self.curve.name:
158 raise ValueError(
159 "peer_public_key and self are not on the same curve"
160 )
162 return _evp_pkey_derive(self._backend, self._evp_pkey, peer_public_key)
164 def public_key(self) -> ec.EllipticCurvePublicKey:
165 group = self._backend._lib.EC_KEY_get0_group(self._ec_key)
166 self._backend.openssl_assert(group != self._backend._ffi.NULL)
168 curve_nid = self._backend._lib.EC_GROUP_get_curve_name(group)
169 public_ec_key = self._backend._ec_key_new_by_curve_nid(curve_nid)
171 point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key)
172 self._backend.openssl_assert(point != self._backend._ffi.NULL)
174 res = self._backend._lib.EC_KEY_set_public_key(public_ec_key, point)
175 self._backend.openssl_assert(res == 1)
177 evp_pkey = self._backend._ec_cdata_to_evp_pkey(public_ec_key)
179 return _EllipticCurvePublicKey(self._backend, public_ec_key, evp_pkey)
181 def private_numbers(self) -> ec.EllipticCurvePrivateNumbers:
182 bn = self._backend._lib.EC_KEY_get0_private_key(self._ec_key)
183 private_value = self._backend._bn_to_int(bn)
184 return ec.EllipticCurvePrivateNumbers(
185 private_value=private_value,
186 public_numbers=self.public_key().public_numbers(),
187 )
189 def private_bytes(
190 self,
191 encoding: serialization.Encoding,
192 format: serialization.PrivateFormat,
193 encryption_algorithm: serialization.KeySerializationEncryption,
194 ) -> bytes:
195 return self._backend._private_key_bytes(
196 encoding,
197 format,
198 encryption_algorithm,
199 self,
200 self._evp_pkey,
201 self._ec_key,
202 )
204 def sign(
205 self,
206 data: bytes,
207 signature_algorithm: ec.EllipticCurveSignatureAlgorithm,
208 ) -> bytes:
209 _check_signature_algorithm(signature_algorithm)
210 data, _ = _calculate_digest_and_algorithm(
211 data,
212 signature_algorithm.algorithm,
213 )
214 return _ecdsa_sig_sign(self._backend, self, data)
217class _EllipticCurvePublicKey(ec.EllipticCurvePublicKey):
218 def __init__(self, backend: "Backend", ec_key_cdata, evp_pkey):
219 self._backend = backend
220 self._ec_key = ec_key_cdata
221 self._evp_pkey = evp_pkey
223 sn = _ec_key_curve_sn(backend, ec_key_cdata)
224 self._curve = _sn_to_elliptic_curve(backend, sn)
225 _mark_asn1_named_ec_curve(backend, ec_key_cdata)
226 _check_key_infinity(backend, ec_key_cdata)
228 @property
229 def curve(self) -> ec.EllipticCurve:
230 return self._curve
232 @property
233 def key_size(self) -> int:
234 return self.curve.key_size
236 def public_numbers(self) -> ec.EllipticCurvePublicNumbers:
237 get_func, group = self._backend._ec_key_determine_group_get_func(
238 self._ec_key
239 )
240 point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key)
241 self._backend.openssl_assert(point != self._backend._ffi.NULL)
243 with self._backend._tmp_bn_ctx() as bn_ctx:
244 bn_x = self._backend._lib.BN_CTX_get(bn_ctx)
245 bn_y = self._backend._lib.BN_CTX_get(bn_ctx)
247 res = get_func(group, point, bn_x, bn_y, bn_ctx)
248 self._backend.openssl_assert(res == 1)
250 x = self._backend._bn_to_int(bn_x)
251 y = self._backend._bn_to_int(bn_y)
253 return ec.EllipticCurvePublicNumbers(x=x, y=y, curve=self._curve)
255 def _encode_point(self, format: serialization.PublicFormat) -> bytes:
256 if format is serialization.PublicFormat.CompressedPoint:
257 conversion = self._backend._lib.POINT_CONVERSION_COMPRESSED
258 else:
259 assert format is serialization.PublicFormat.UncompressedPoint
260 conversion = self._backend._lib.POINT_CONVERSION_UNCOMPRESSED
262 group = self._backend._lib.EC_KEY_get0_group(self._ec_key)
263 self._backend.openssl_assert(group != self._backend._ffi.NULL)
264 point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key)
265 self._backend.openssl_assert(point != self._backend._ffi.NULL)
266 with self._backend._tmp_bn_ctx() as bn_ctx:
267 buflen = self._backend._lib.EC_POINT_point2oct(
268 group, point, conversion, self._backend._ffi.NULL, 0, bn_ctx
269 )
270 self._backend.openssl_assert(buflen > 0)
271 buf = self._backend._ffi.new("char[]", buflen)
272 res = self._backend._lib.EC_POINT_point2oct(
273 group, point, conversion, buf, buflen, bn_ctx
274 )
275 self._backend.openssl_assert(buflen == res)
277 return self._backend._ffi.buffer(buf)[:]
279 def public_bytes(
280 self,
281 encoding: serialization.Encoding,
282 format: serialization.PublicFormat,
283 ) -> bytes:
284 if (
285 encoding is serialization.Encoding.X962
286 or format is serialization.PublicFormat.CompressedPoint
287 or format is serialization.PublicFormat.UncompressedPoint
288 ):
289 if encoding is not serialization.Encoding.X962 or format not in (
290 serialization.PublicFormat.CompressedPoint,
291 serialization.PublicFormat.UncompressedPoint,
292 ):
293 raise ValueError(
294 "X962 encoding must be used with CompressedPoint or "
295 "UncompressedPoint format"
296 )
298 return self._encode_point(format)
299 else:
300 return self._backend._public_key_bytes(
301 encoding, format, self, self._evp_pkey, None
302 )
304 def verify(
305 self,
306 signature: bytes,
307 data: bytes,
308 signature_algorithm: ec.EllipticCurveSignatureAlgorithm,
309 ) -> None:
310 _check_signature_algorithm(signature_algorithm)
311 data, _ = _calculate_digest_and_algorithm(
312 data,
313 signature_algorithm.algorithm,
314 )
315 _ecdsa_sig_verify(self._backend, self, signature, data)