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

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. 

4 

5import typing 

6 

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 

18 

19if typing.TYPE_CHECKING: 

20 from cryptography.hazmat.backends.openssl.backend import Backend 

21 

22 

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 ) 

31 

32 

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) 

36 

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 ) 

44 

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 ) 

55 

56 curve_name = backend._lib.OBJ_nid2sn(nid) 

57 backend.openssl_assert(curve_name != backend._ffi.NULL) 

58 

59 sn = backend._ffi.string(curve_name).decode("ascii") 

60 return sn 

61 

62 

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

69 

70 backend._lib.EC_KEY_set_asn1_flag( 

71 ec_cdata, backend._lib.OPENSSL_EC_NAMED_CURVE 

72 ) 

73 

74 

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 ) 

84 

85 

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 ) 

94 

95 

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) 

101 

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]] 

109 

110 

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 

123 

124 

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 

130 

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) 

135 

136 @property 

137 def curve(self) -> ec.EllipticCurve: 

138 return self._curve 

139 

140 @property 

141 def key_size(self) -> int: 

142 return self.curve.key_size 

143 

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 ) 

156 

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 ) 

161 

162 return _evp_pkey_derive(self._backend, self._evp_pkey, peer_public_key) 

163 

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) 

167 

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) 

170 

171 point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key) 

172 self._backend.openssl_assert(point != self._backend._ffi.NULL) 

173 

174 res = self._backend._lib.EC_KEY_set_public_key(public_ec_key, point) 

175 self._backend.openssl_assert(res == 1) 

176 

177 evp_pkey = self._backend._ec_cdata_to_evp_pkey(public_ec_key) 

178 

179 return _EllipticCurvePublicKey(self._backend, public_ec_key, evp_pkey) 

180 

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 ) 

188 

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 ) 

203 

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) 

215 

216 

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 

222 

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) 

227 

228 @property 

229 def curve(self) -> ec.EllipticCurve: 

230 return self._curve 

231 

232 @property 

233 def key_size(self) -> int: 

234 return self.curve.key_size 

235 

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) 

242 

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) 

246 

247 res = get_func(group, point, bn_x, bn_y, bn_ctx) 

248 self._backend.openssl_assert(res == 1) 

249 

250 x = self._backend._bn_to_int(bn_x) 

251 y = self._backend._bn_to_int(bn_y) 

252 

253 return ec.EllipticCurvePublicNumbers(x=x, y=y, curve=self._curve) 

254 

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 

261 

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) 

276 

277 return self._backend._ffi.buffer(buf)[:] 

278 

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 ) 

297 

298 return self._encode_point(format) 

299 else: 

300 return self._backend._public_key_bytes( 

301 encoding, format, self, self._evp_pkey, None 

302 ) 

303 

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)