Skip to content

Commit f38eb4a

Browse files
authored
Migrate EC support to Rust (#9024)
1 parent 31c3f2d commit f38eb4a

File tree

12 files changed

+695
-614
lines changed

12 files changed

+695
-614
lines changed

src/cryptography/hazmat/backends/openssl/backend.py

Lines changed: 17 additions & 244 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,12 @@
88
import contextlib
99
import itertools
1010
import typing
11-
from contextlib import contextmanager
1211

1312
from cryptography import utils, x509
1413
from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
1514
from cryptography.hazmat.backends.openssl import aead
1615
from cryptography.hazmat.backends.openssl.ciphers import _CipherContext
1716
from cryptography.hazmat.backends.openssl.cmac import _CMACContext
18-
from cryptography.hazmat.backends.openssl.ec import (
19-
_EllipticCurvePrivateKey,
20-
_EllipticCurvePublicKey,
21-
)
2217
from cryptography.hazmat.backends.openssl.rsa import (
2318
_RSAPrivateKey,
2419
_RSAPublicKey,
@@ -542,10 +537,9 @@ def _evp_pkey_to_private_key(
542537
int(self._ffi.cast("uintptr_t", evp_pkey))
543538
)
544539
elif key_type == self._lib.EVP_PKEY_EC:
545-
ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey)
546-
self.openssl_assert(ec_cdata != self._ffi.NULL)
547-
ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free)
548-
return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey)
540+
return rust_openssl.ec.private_key_from_ptr(
541+
int(self._ffi.cast("uintptr_t", evp_pkey))
542+
)
549543
elif key_type in self._dh_types:
550544
return rust_openssl.dh.private_key_from_ptr(
551545
int(self._ffi.cast("uintptr_t", evp_pkey))
@@ -603,12 +597,9 @@ def _evp_pkey_to_public_key(self, evp_pkey) -> PublicKeyTypes:
603597
int(self._ffi.cast("uintptr_t", evp_pkey))
604598
)
605599
elif key_type == self._lib.EVP_PKEY_EC:
606-
ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey)
607-
if ec_cdata == self._ffi.NULL:
608-
errors = self._consume_errors()
609-
raise ValueError("Unable to load EC key", errors)
610-
ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free)
611-
return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey)
600+
return rust_openssl.ec.public_key_from_ptr(
601+
int(self._ffi.cast("uintptr_t", evp_pkey))
602+
)
612603
elif key_type in self._dh_types:
613604
return rust_openssl.dh.public_key_from_ptr(
614605
int(self._ffi.cast("uintptr_t", evp_pkey))
@@ -944,20 +935,7 @@ def elliptic_curve_supported(self, curve: ec.EllipticCurve) -> bool:
944935
):
945936
return False
946937

947-
try:
948-
curve_nid = self._elliptic_curve_to_nid(curve)
949-
except UnsupportedAlgorithm:
950-
curve_nid = self._lib.NID_undef
951-
952-
group = self._lib.EC_GROUP_new_by_curve_name(curve_nid)
953-
954-
if group == self._ffi.NULL:
955-
self._consume_errors()
956-
return False
957-
else:
958-
self.openssl_assert(curve_nid != self._lib.NID_undef)
959-
self._lib.EC_GROUP_free(group)
960-
return True
938+
return rust_openssl.ec.curve_supported(curve)
961939

962940
def elliptic_curve_signature_algorithm_supported(
963941
self,
@@ -979,158 +957,27 @@ def generate_elliptic_curve_private_key(
979957
"""
980958
Generate a new private key on the named curve.
981959
"""
982-
983-
if self.elliptic_curve_supported(curve):
984-
ec_cdata = self._ec_key_new_by_curve(curve)
985-
986-
res = self._lib.EC_KEY_generate_key(ec_cdata)
987-
self.openssl_assert(res == 1)
988-
989-
evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata)
990-
991-
return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey)
992-
else:
993-
raise UnsupportedAlgorithm(
994-
f"Backend object does not support {curve.name}.",
995-
_Reasons.UNSUPPORTED_ELLIPTIC_CURVE,
996-
)
960+
return rust_openssl.ec.generate_private_key(curve)
997961

998962
def load_elliptic_curve_private_numbers(
999963
self, numbers: ec.EllipticCurvePrivateNumbers
1000964
) -> ec.EllipticCurvePrivateKey:
1001-
public = numbers.public_numbers
1002-
1003-
ec_cdata = self._ec_key_new_by_curve(public.curve)
1004-
1005-
private_value = self._ffi.gc(
1006-
self._int_to_bn(numbers.private_value), self._lib.BN_clear_free
1007-
)
1008-
res = self._lib.EC_KEY_set_private_key(ec_cdata, private_value)
1009-
if res != 1:
1010-
self._consume_errors()
1011-
raise ValueError("Invalid EC key.")
1012-
1013-
with self._tmp_bn_ctx() as bn_ctx:
1014-
self._ec_key_set_public_key_affine_coordinates(
1015-
ec_cdata, public.x, public.y, bn_ctx
1016-
)
1017-
# derive the expected public point and compare it to the one we
1018-
# just set based on the values we were given. If they don't match
1019-
# this isn't a valid key pair.
1020-
group = self._lib.EC_KEY_get0_group(ec_cdata)
1021-
self.openssl_assert(group != self._ffi.NULL)
1022-
set_point = backend._lib.EC_KEY_get0_public_key(ec_cdata)
1023-
self.openssl_assert(set_point != self._ffi.NULL)
1024-
computed_point = self._lib.EC_POINT_new(group)
1025-
self.openssl_assert(computed_point != self._ffi.NULL)
1026-
computed_point = self._ffi.gc(
1027-
computed_point, self._lib.EC_POINT_free
1028-
)
1029-
res = self._lib.EC_POINT_mul(
1030-
group,
1031-
computed_point,
1032-
private_value,
1033-
self._ffi.NULL,
1034-
self._ffi.NULL,
1035-
bn_ctx,
1036-
)
1037-
self.openssl_assert(res == 1)
1038-
if (
1039-
self._lib.EC_POINT_cmp(
1040-
group, set_point, computed_point, bn_ctx
1041-
)
1042-
!= 0
1043-
):
1044-
raise ValueError("Invalid EC key.")
1045-
1046-
evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata)
1047-
1048-
return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey)
965+
return rust_openssl.ec.from_private_numbers(numbers)
1049966

1050967
def load_elliptic_curve_public_numbers(
1051968
self, numbers: ec.EllipticCurvePublicNumbers
1052969
) -> ec.EllipticCurvePublicKey:
1053-
ec_cdata = self._ec_key_new_by_curve(numbers.curve)
1054-
with self._tmp_bn_ctx() as bn_ctx:
1055-
self._ec_key_set_public_key_affine_coordinates(
1056-
ec_cdata, numbers.x, numbers.y, bn_ctx
1057-
)
1058-
evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata)
1059-
1060-
return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey)
970+
return rust_openssl.ec.from_public_numbers(numbers)
1061971

1062972
def load_elliptic_curve_public_bytes(
1063973
self, curve: ec.EllipticCurve, point_bytes: bytes
1064974
) -> ec.EllipticCurvePublicKey:
1065-
ec_cdata = self._ec_key_new_by_curve(curve)
1066-
group = self._lib.EC_KEY_get0_group(ec_cdata)
1067-
self.openssl_assert(group != self._ffi.NULL)
1068-
point = self._lib.EC_POINT_new(group)
1069-
self.openssl_assert(point != self._ffi.NULL)
1070-
point = self._ffi.gc(point, self._lib.EC_POINT_free)
1071-
with self._tmp_bn_ctx() as bn_ctx:
1072-
res = self._lib.EC_POINT_oct2point(
1073-
group, point, point_bytes, len(point_bytes), bn_ctx
1074-
)
1075-
if res != 1:
1076-
self._consume_errors()
1077-
raise ValueError("Invalid public bytes for the given curve")
1078-
1079-
res = self._lib.EC_KEY_set_public_key(ec_cdata, point)
1080-
self.openssl_assert(res == 1)
1081-
evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata)
1082-
return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey)
975+
return rust_openssl.ec.from_public_bytes(curve, point_bytes)
1083976

1084977
def derive_elliptic_curve_private_key(
1085978
self, private_value: int, curve: ec.EllipticCurve
1086979
) -> ec.EllipticCurvePrivateKey:
1087-
ec_cdata = self._ec_key_new_by_curve(curve)
1088-
1089-
group = self._lib.EC_KEY_get0_group(ec_cdata)
1090-
self.openssl_assert(group != self._ffi.NULL)
1091-
1092-
point = self._lib.EC_POINT_new(group)
1093-
self.openssl_assert(point != self._ffi.NULL)
1094-
point = self._ffi.gc(point, self._lib.EC_POINT_free)
1095-
1096-
value = self._int_to_bn(private_value)
1097-
value = self._ffi.gc(value, self._lib.BN_clear_free)
1098-
1099-
with self._tmp_bn_ctx() as bn_ctx:
1100-
res = self._lib.EC_POINT_mul(
1101-
group, point, value, self._ffi.NULL, self._ffi.NULL, bn_ctx
1102-
)
1103-
self.openssl_assert(res == 1)
1104-
1105-
bn_x = self._lib.BN_CTX_get(bn_ctx)
1106-
bn_y = self._lib.BN_CTX_get(bn_ctx)
1107-
1108-
res = self._lib.EC_POINT_get_affine_coordinates(
1109-
group, point, bn_x, bn_y, bn_ctx
1110-
)
1111-
if res != 1:
1112-
self._consume_errors()
1113-
raise ValueError("Unable to derive key from private_value")
1114-
1115-
res = self._lib.EC_KEY_set_public_key(ec_cdata, point)
1116-
self.openssl_assert(res == 1)
1117-
private = self._int_to_bn(private_value)
1118-
private = self._ffi.gc(private, self._lib.BN_clear_free)
1119-
res = self._lib.EC_KEY_set_private_key(ec_cdata, private)
1120-
self.openssl_assert(res == 1)
1121-
1122-
evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata)
1123-
1124-
return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey)
1125-
1126-
def _ec_key_new_by_curve(self, curve: ec.EllipticCurve):
1127-
curve_nid = self._elliptic_curve_to_nid(curve)
1128-
return self._ec_key_new_by_curve_nid(curve_nid)
1129-
1130-
def _ec_key_new_by_curve_nid(self, curve_nid: int):
1131-
ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid)
1132-
self.openssl_assert(ec_cdata != self._ffi.NULL)
1133-
return self._ffi.gc(ec_cdata, self._lib.EC_KEY_free)
980+
return rust_openssl.ec.derive_private_key(private_value, curve)
1134981

1135982
def elliptic_curve_exchange_algorithm_supported(
1136983
self, algorithm: ec.ECDH, curve: ec.EllipticCurve
@@ -1139,73 +986,6 @@ def elliptic_curve_exchange_algorithm_supported(
1139986
algorithm, ec.ECDH
1140987
)
1141988

1142-
def _ec_cdata_to_evp_pkey(self, ec_cdata):
1143-
evp_pkey = self._create_evp_pkey_gc()
1144-
res = self._lib.EVP_PKEY_set1_EC_KEY(evp_pkey, ec_cdata)
1145-
self.openssl_assert(res == 1)
1146-
return evp_pkey
1147-
1148-
def _elliptic_curve_to_nid(self, curve: ec.EllipticCurve) -> int:
1149-
"""
1150-
Get the NID for a curve name.
1151-
"""
1152-
1153-
curve_aliases = {"secp192r1": "prime192v1", "secp256r1": "prime256v1"}
1154-
1155-
curve_name = curve_aliases.get(curve.name, curve.name)
1156-
1157-
curve_nid = self._lib.OBJ_sn2nid(curve_name.encode())
1158-
if curve_nid == self._lib.NID_undef:
1159-
raise UnsupportedAlgorithm(
1160-
f"{curve.name} is not a supported elliptic curve",
1161-
_Reasons.UNSUPPORTED_ELLIPTIC_CURVE,
1162-
)
1163-
return curve_nid
1164-
1165-
@contextmanager
1166-
def _tmp_bn_ctx(self):
1167-
bn_ctx = self._lib.BN_CTX_new()
1168-
self.openssl_assert(bn_ctx != self._ffi.NULL)
1169-
bn_ctx = self._ffi.gc(bn_ctx, self._lib.BN_CTX_free)
1170-
self._lib.BN_CTX_start(bn_ctx)
1171-
try:
1172-
yield bn_ctx
1173-
finally:
1174-
self._lib.BN_CTX_end(bn_ctx)
1175-
1176-
def _ec_key_set_public_key_affine_coordinates(
1177-
self,
1178-
ec_cdata,
1179-
x: int,
1180-
y: int,
1181-
bn_ctx,
1182-
) -> None:
1183-
"""
1184-
Sets the public key point in the EC_KEY context to the affine x and y
1185-
values.
1186-
"""
1187-
1188-
if x < 0 or y < 0:
1189-
raise ValueError(
1190-
"Invalid EC key. Both x and y must be non-negative."
1191-
)
1192-
1193-
x = self._ffi.gc(self._int_to_bn(x), self._lib.BN_free)
1194-
y = self._ffi.gc(self._int_to_bn(y), self._lib.BN_free)
1195-
group = self._lib.EC_KEY_get0_group(ec_cdata)
1196-
self.openssl_assert(group != self._ffi.NULL)
1197-
point = self._lib.EC_POINT_new(group)
1198-
self.openssl_assert(point != self._ffi.NULL)
1199-
point = self._ffi.gc(point, self._lib.EC_POINT_free)
1200-
res = self._lib.EC_POINT_set_affine_coordinates(
1201-
group, point, x, y, bn_ctx
1202-
)
1203-
if res != 1:
1204-
self._consume_errors()
1205-
raise ValueError("Invalid EC key.")
1206-
res = self._lib.EC_KEY_set_public_key(ec_cdata, point)
1207-
self.openssl_assert(res == 1)
1208-
1209989
def _private_key_bytes(
1210990
self,
1211991
encoding: serialization.Encoding,
@@ -1278,11 +1058,8 @@ def _private_key_bytes(
12781058
key_type = self._lib.EVP_PKEY_id(evp_pkey)
12791059

12801060
if encoding is serialization.Encoding.PEM:
1281-
if key_type == self._lib.EVP_PKEY_RSA:
1282-
write_bio = self._lib.PEM_write_bio_RSAPrivateKey
1283-
else:
1284-
assert key_type == self._lib.EVP_PKEY_EC
1285-
write_bio = self._lib.PEM_write_bio_ECPrivateKey
1061+
assert key_type == self._lib.EVP_PKEY_RSA
1062+
write_bio = self._lib.PEM_write_bio_RSAPrivateKey
12861063
return self._private_key_bytes_via_bio(
12871064
write_bio, cdata, password
12881065
)
@@ -1293,11 +1070,8 @@ def _private_key_bytes(
12931070
"Encryption is not supported for DER encoded "
12941071
"traditional OpenSSL keys"
12951072
)
1296-
if key_type == self._lib.EVP_PKEY_RSA:
1297-
write_bio = self._lib.i2d_RSAPrivateKey_bio
1298-
else:
1299-
assert key_type == self._lib.EVP_PKEY_EC
1300-
write_bio = self._lib.i2d_ECPrivateKey_bio
1073+
assert key_type == self._lib.EVP_PKEY_RSA
1074+
write_bio = self._lib.i2d_RSAPrivateKey_bio
13011075
return self._bio_func_output(write_bio, cdata)
13021076

13031077
raise ValueError("Unsupported encoding for TraditionalOpenSSL")
@@ -1374,8 +1148,7 @@ def _public_key_bytes(
13741148
if format is serialization.PublicFormat.PKCS1:
13751149
# Only RSA is supported here.
13761150
key_type = self._lib.EVP_PKEY_id(evp_pkey)
1377-
if key_type != self._lib.EVP_PKEY_RSA:
1378-
raise ValueError("PKCS1 format is supported only for RSA keys")
1151+
self.openssl_assert(key_type == self._lib.EVP_PKEY_RSA)
13791152

13801153
if encoding is serialization.Encoding.PEM:
13811154
write_bio = self._lib.PEM_write_bio_RSAPublicKey

0 commit comments

Comments
 (0)