Skip to content

Commit fb9cf10

Browse files
alexclaude
andauthored
Migrate Encoding enum from Python to Rust (#14358)
https://claude.ai/code/session_01QJvGwsuX7dJjKuKdwmLLSE Co-authored-by: Claude <noreply@anthropic.com>
1 parent 672ee1a commit fb9cf10

24 files changed

Lines changed: 105 additions & 131 deletions

File tree

src/cryptography/hazmat/bindings/_rust/__init__.pyi

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ class ANSIX923UnpaddingContext(padding.PaddingContext):
2727
def update(self, data: Buffer) -> bytes: ...
2828
def finalize(self) -> bytes: ...
2929

30+
class Encoding:
31+
PEM: typing.ClassVar[Encoding]
32+
DER: typing.ClassVar[Encoding]
33+
OpenSSH: typing.ClassVar[Encoding]
34+
Raw: typing.ClassVar[Encoding]
35+
X962: typing.ClassVar[Encoding]
36+
SMIME: typing.ClassVar[Encoding]
37+
3038
class ObjectIdentifier:
3139
def __init__(self, value: str) -> None: ...
3240
@property

src/cryptography/hazmat/primitives/_serialization.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import abc
88

99
from cryptography import utils
10+
from cryptography.hazmat.bindings._rust import Encoding as Encoding
1011
from cryptography.hazmat.primitives.hashes import HashAlgorithm
1112

1213
# This exists to break an import cycle. These classes are normally accessible
@@ -18,15 +19,6 @@ class PBES(utils.Enum):
1819
PBESv2SHA256AndAES256CBC = "PBESv2 using SHA256 PBKDF2 and AES256 CBC"
1920

2021

21-
class Encoding(utils.Enum):
22-
PEM = "PEM"
23-
DER = "DER"
24-
OpenSSH = "OpenSSH"
25-
Raw = "Raw"
26-
X962 = "ANSI X9.62"
27-
SMIME = "S/MIME"
28-
29-
3022
class PrivateFormat(utils.Enum):
3123
PKCS8 = "PKCS8"
3224
TraditionalOpenSSL = "TraditionalOpenSSL"

src/rust/src/asn1.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use pyo3::types::{IntoPyDict, PyAnyMethods};
88
use pyo3::IntoPyObject;
99

1010
use crate::error::{CryptographyError, CryptographyResult};
11-
use crate::types;
11+
use crate::serialization::Encoding;
1212

1313
pub(crate) fn py_oid_to_oid(
1414
py_oid: pyo3::Bound<'_, pyo3::PyAny>,
@@ -102,24 +102,22 @@ pub(crate) fn encode_der_data<'p>(
102102
py: pyo3::Python<'p>,
103103
pem_tag: String,
104104
data: Vec<u8>,
105-
encoding: &pyo3::Bound<'p, pyo3::PyAny>,
105+
encoding: Encoding,
106106
) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
107-
if encoding.is(&types::ENCODING_DER.get(py)?) {
108-
Ok(pyo3::types::PyBytes::new(py, &data))
109-
} else if encoding.is(&types::ENCODING_PEM.get(py)?) {
110-
Ok(pyo3::types::PyBytes::new(
107+
match encoding {
108+
Encoding::DER => Ok(pyo3::types::PyBytes::new(py, &data)),
109+
Encoding::PEM => Ok(pyo3::types::PyBytes::new(
111110
py,
112111
&pem::encode_config(
113112
&pem::Pem::new(pem_tag, data),
114113
cryptography_key_parsing::pem::ENCODE_CONFIG,
115114
)
116115
.into_bytes(),
117-
))
118-
} else {
119-
Err(
120-
pyo3::exceptions::PyTypeError::new_err("encoding must be Encoding.DER or Encoding.PEM")
121-
.into(),
116+
)),
117+
_ => Err(pyo3::exceptions::PyTypeError::new_err(
118+
"encoding must be Encoding.DER or Encoding.PEM",
122119
)
120+
.into()),
123121
}
124122
}
125123

src/rust/src/backend/dh.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ impl DHPrivateKey {
220220
fn private_bytes<'p>(
221221
slf: &pyo3::Bound<'p, Self>,
222222
py: pyo3::Python<'p>,
223-
encoding: &pyo3::Bound<'p, pyo3::PyAny>,
223+
encoding: crate::serialization::Encoding,
224224
format: &pyo3::Bound<'p, pyo3::PyAny>,
225225
encryption_algorithm: &pyo3::Bound<'p, pyo3::PyAny>,
226226
) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
@@ -266,7 +266,7 @@ impl DHPublicKey {
266266
fn public_bytes<'p>(
267267
slf: &pyo3::Bound<'p, Self>,
268268
py: pyo3::Python<'p>,
269-
encoding: &pyo3::Bound<'p, pyo3::PyAny>,
269+
encoding: crate::serialization::Encoding,
270270
format: &pyo3::Bound<'p, pyo3::PyAny>,
271271
) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
272272
if !format.is(&types::PUBLIC_FORMAT_SUBJECT_PUBLIC_KEY_INFO.get(py)?) {
@@ -359,7 +359,7 @@ impl DHParameters {
359359
fn parameter_bytes<'p>(
360360
&self,
361361
py: pyo3::Python<'p>,
362-
encoding: pyo3::Bound<'p, pyo3::PyAny>,
362+
encoding: crate::serialization::Encoding,
363363
format: pyo3::Bound<'p, pyo3::PyAny>,
364364
) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
365365
if !format.is(&types::PARAMETER_FORMAT_PKCS3.get(py)?) {
@@ -386,7 +386,7 @@ impl DHParameters {
386386
} else {
387387
"X9.42 DH PARAMETERS"
388388
};
389-
encode_der_data(py, tag.to_string(), data, &encoding)
389+
encode_der_data(py, tag.to_string(), data, encoding)
390390
}
391391
}
392392

src/rust/src/backend/dsa.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ impl DsaPrivateKey {
137137
fn private_bytes<'p>(
138138
slf: &pyo3::Bound<'p, Self>,
139139
py: pyo3::Python<'p>,
140-
encoding: &pyo3::Bound<'p, pyo3::PyAny>,
140+
encoding: crate::serialization::Encoding,
141141
format: &pyo3::Bound<'p, pyo3::PyAny>,
142142
encryption_algorithm: &pyo3::Bound<'p, pyo3::PyAny>,
143143
) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
@@ -223,7 +223,7 @@ impl DsaPublicKey {
223223
fn public_bytes<'p>(
224224
slf: &pyo3::Bound<'p, Self>,
225225
py: pyo3::Python<'p>,
226-
encoding: &pyo3::Bound<'p, pyo3::PyAny>,
226+
encoding: crate::serialization::Encoding,
227227
format: &pyo3::Bound<'p, pyo3::PyAny>,
228228
) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
229229
utils::pkey_public_bytes(py, slf, &slf.borrow().pkey, encoding, format, true, false)

src/rust/src/backend/ec.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ impl ECPrivateKey {
349349
fn private_bytes<'p>(
350350
slf: &pyo3::Bound<'p, Self>,
351351
py: pyo3::Python<'p>,
352-
encoding: &pyo3::Bound<'p, pyo3::PyAny>,
352+
encoding: crate::serialization::Encoding,
353353
format: &pyo3::Bound<'p, pyo3::PyAny>,
354354
encryption_algorithm: &pyo3::Bound<'p, pyo3::PyAny>,
355355
) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
@@ -459,7 +459,7 @@ impl ECPublicKey {
459459
fn public_bytes<'p>(
460460
slf: &pyo3::Bound<'p, Self>,
461461
py: pyo3::Python<'p>,
462-
encoding: &pyo3::Bound<'p, pyo3::PyAny>,
462+
encoding: crate::serialization::Encoding,
463463
format: &pyo3::Bound<'p, pyo3::PyAny>,
464464
) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
465465
utils::pkey_public_bytes(py, slf, &slf.borrow().pkey, encoding, format, true, false)

src/rust/src/backend/ed25519.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ impl Ed25519PrivateKey {
100100
fn private_bytes<'p>(
101101
slf: &pyo3::Bound<'p, Self>,
102102
py: pyo3::Python<'p>,
103-
encoding: &pyo3::Bound<'p, pyo3::PyAny>,
103+
encoding: crate::serialization::Encoding,
104104
format: &pyo3::Bound<'p, pyo3::PyAny>,
105105
encryption_algorithm: &pyo3::Bound<'p, pyo3::PyAny>,
106106
) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
@@ -155,7 +155,7 @@ impl Ed25519PublicKey {
155155
fn public_bytes<'p>(
156156
slf: &pyo3::Bound<'p, Self>,
157157
py: pyo3::Python<'p>,
158-
encoding: &pyo3::Bound<'p, pyo3::PyAny>,
158+
encoding: crate::serialization::Encoding,
159159
format: &pyo3::Bound<'p, pyo3::PyAny>,
160160
) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
161161
utils::pkey_public_bytes(py, slf, &slf.borrow().pkey, encoding, format, true, true)

src/rust/src/backend/ed448.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ impl Ed448PrivateKey {
9898
fn private_bytes<'p>(
9999
slf: &pyo3::Bound<'p, Self>,
100100
py: pyo3::Python<'p>,
101-
encoding: &pyo3::Bound<'p, pyo3::PyAny>,
101+
encoding: crate::serialization::Encoding,
102102
format: &pyo3::Bound<'p, pyo3::PyAny>,
103103
encryption_algorithm: &pyo3::Bound<'p, pyo3::PyAny>,
104104
) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
@@ -152,7 +152,7 @@ impl Ed448PublicKey {
152152
fn public_bytes<'p>(
153153
slf: &pyo3::Bound<'p, Self>,
154154
py: pyo3::Python<'p>,
155-
encoding: &pyo3::Bound<'p, pyo3::PyAny>,
155+
encoding: crate::serialization::Encoding,
156156
format: &pyo3::Bound<'p, pyo3::PyAny>,
157157
) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
158158
utils::pkey_public_bytes(py, slf, &slf.borrow().pkey, encoding, format, true, true)

src/rust/src/backend/rsa.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ impl RsaPrivateKey {
411411
fn private_bytes<'p>(
412412
slf: &pyo3::Bound<'p, Self>,
413413
py: pyo3::Python<'p>,
414-
encoding: &pyo3::Bound<'p, pyo3::PyAny>,
414+
encoding: crate::serialization::Encoding,
415415
format: &pyo3::Bound<'p, pyo3::PyAny>,
416416
encryption_algorithm: &pyo3::Bound<'p, pyo3::PyAny>,
417417
) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
@@ -550,7 +550,7 @@ impl RsaPublicKey {
550550
fn public_bytes<'p>(
551551
slf: &pyo3::Bound<'p, Self>,
552552
py: pyo3::Python<'p>,
553-
encoding: &pyo3::Bound<'p, pyo3::PyAny>,
553+
encoding: crate::serialization::Encoding,
554554
format: &pyo3::Bound<'p, pyo3::PyAny>,
555555
) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
556556
utils::pkey_public_bytes(py, slf, &slf.borrow().pkey, encoding, format, true, false)

src/rust/src/backend/utils.rs

Lines changed: 16 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use pyo3::types::{PyAnyMethods, PyBytesMethods};
66

77
use crate::backend::hashes::Hash;
88
use crate::error::{CryptographyError, CryptographyResult};
9+
use crate::serialization::Encoding;
910
use crate::types;
1011

1112
pub(crate) fn py_int_to_bn(
@@ -42,19 +43,12 @@ pub(crate) fn pkey_private_bytes<'p>(
4243
py: pyo3::Python<'p>,
4344
key_obj: &pyo3::Bound<'p, pyo3::PyAny>,
4445
pkey: &openssl::pkey::PKey<openssl::pkey::Private>,
45-
encoding: &pyo3::Bound<'p, pyo3::PyAny>,
46+
encoding: Encoding,
4647
format: &pyo3::Bound<'p, pyo3::PyAny>,
4748
encryption_algorithm: &pyo3::Bound<'p, pyo3::PyAny>,
4849
openssh_allowed: bool,
4950
raw_allowed: bool,
5051
) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
51-
if !encoding.is_instance(&types::ENCODING.get(py)?)? {
52-
return Err(CryptographyError::from(
53-
pyo3::exceptions::PyTypeError::new_err(
54-
"encoding must be an item from the Encoding enum",
55-
),
56-
));
57-
}
5852
if !format.is_instance(&types::PRIVATE_FORMAT.get(py)?)? {
5953
return Err(CryptographyError::from(
6054
pyo3::exceptions::PyTypeError::new_err(
@@ -70,11 +64,9 @@ pub(crate) fn pkey_private_bytes<'p>(
7064
));
7165
}
7266

73-
if raw_allowed
74-
&& (encoding.is(&types::ENCODING_RAW.get(py)?)
75-
|| format.is(&types::PRIVATE_FORMAT_RAW.get(py)?))
67+
if raw_allowed && (encoding == Encoding::Raw || format.is(&types::PRIVATE_FORMAT_RAW.get(py)?))
7668
{
77-
if !encoding.is(&types::ENCODING_RAW.get(py)?)
69+
if encoding != Encoding::Raw
7870
|| !format.is(&types::PRIVATE_FORMAT_RAW.get(py)?)
7971
|| !encryption_algorithm.is_instance(&types::NO_ENCRYPTION.get(py)?)?
8072
{
@@ -139,14 +131,14 @@ pub(crate) fn pkey_private_bytes<'p>(
139131
}
140132
if let Ok(rsa) = pkey.rsa() {
141133
let der_bytes = cryptography_key_parsing::rsa::serialize_pkcs1_private_key(&rsa)?;
142-
if encoding.is(&types::ENCODING_PEM.get(py)?) {
134+
if encoding == Encoding::PEM {
143135
let pem_bytes = cryptography_key_parsing::pem::encrypt_pem(
144136
"RSA PRIVATE KEY",
145137
&der_bytes,
146138
password,
147139
)?;
148140
return Ok(pyo3::types::PyBytes::new(py, &pem_bytes));
149-
} else if encoding.is(&types::ENCODING_DER.get(py)?) {
141+
} else if encoding == Encoding::DER {
150142
if !password.is_empty() {
151143
return Err(CryptographyError::from(
152144
pyo3::exceptions::PyValueError::new_err(
@@ -159,14 +151,14 @@ pub(crate) fn pkey_private_bytes<'p>(
159151
}
160152
} else if let Ok(dsa) = pkey.dsa() {
161153
let der_bytes = cryptography_key_parsing::dsa::serialize_pkcs1_private_key(&dsa)?;
162-
if encoding.is(&types::ENCODING_PEM.get(py)?) {
154+
if encoding == Encoding::PEM {
163155
let pem_bytes = cryptography_key_parsing::pem::encrypt_pem(
164156
"DSA PRIVATE KEY",
165157
&der_bytes,
166158
password,
167159
)?;
168160
return Ok(pyo3::types::PyBytes::new(py, &pem_bytes));
169-
} else if encoding.is(&types::ENCODING_DER.get(py)?) {
161+
} else if encoding == Encoding::DER {
170162
if !password.is_empty() {
171163
return Err(CryptographyError::from(
172164
pyo3::exceptions::PyValueError::new_err(
@@ -179,14 +171,14 @@ pub(crate) fn pkey_private_bytes<'p>(
179171
}
180172
} else if let Ok(ec) = pkey.ec_key() {
181173
let der_bytes = cryptography_key_parsing::ec::serialize_pkcs1_private_key(&ec, true)?;
182-
if encoding.is(&types::ENCODING_PEM.get(py)?) {
174+
if encoding == Encoding::PEM {
183175
let pem_bytes = cryptography_key_parsing::pem::encrypt_pem(
184176
"EC PRIVATE KEY",
185177
&der_bytes,
186178
password,
187179
)?;
188180
return Ok(pyo3::types::PyBytes::new(py, &pem_bytes));
189-
} else if encoding.is(&types::ENCODING_DER.get(py)?) {
181+
} else if encoding == Encoding::DER {
190182
if !password.is_empty() {
191183
return Err(CryptographyError::from(
192184
pyo3::exceptions::PyValueError::new_err(
@@ -202,7 +194,7 @@ pub(crate) fn pkey_private_bytes<'p>(
202194

203195
// OpenSSH + PEM
204196
if openssh_allowed && format.is(&types::PRIVATE_FORMAT_OPENSSH.get(py)?) {
205-
if encoding.is(&types::ENCODING_PEM.get(py)?) {
197+
if encoding == Encoding::PEM {
206198
return Ok(types::SERIALIZE_SSH_PRIVATE_KEY
207199
.get(py)?
208200
.call1((key_obj, password, encryption_algorithm))?
@@ -225,18 +217,11 @@ pub(crate) fn pkey_public_bytes<'p>(
225217
py: pyo3::Python<'p>,
226218
key_obj: &pyo3::Bound<'p, pyo3::PyAny>,
227219
pkey: &openssl::pkey::PKey<openssl::pkey::Public>,
228-
encoding: &pyo3::Bound<'p, pyo3::PyAny>,
220+
encoding: Encoding,
229221
format: &pyo3::Bound<'p, pyo3::PyAny>,
230222
openssh_allowed: bool,
231223
raw_allowed: bool,
232224
) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
233-
if !encoding.is_instance(&types::ENCODING.get(py)?)? {
234-
return Err(CryptographyError::from(
235-
pyo3::exceptions::PyTypeError::new_err(
236-
"encoding must be an item from the Encoding enum",
237-
),
238-
));
239-
}
240225
if !format.is_instance(&types::PUBLIC_FORMAT.get(py)?)? {
241226
return Err(CryptographyError::from(
242227
pyo3::exceptions::PyTypeError::new_err(
@@ -245,13 +230,8 @@ pub(crate) fn pkey_public_bytes<'p>(
245230
));
246231
}
247232

248-
if raw_allowed
249-
&& (encoding.is(&types::ENCODING_RAW.get(py)?)
250-
|| format.is(&types::PUBLIC_FORMAT_RAW.get(py)?))
251-
{
252-
if !encoding.is(&types::ENCODING_RAW.get(py)?)
253-
|| !format.is(&types::PUBLIC_FORMAT_RAW.get(py)?)
254-
{
233+
if raw_allowed && (encoding == Encoding::Raw || format.is(&types::PUBLIC_FORMAT_RAW.get(py)?)) {
234+
if encoding != Encoding::Raw || !format.is(&types::PUBLIC_FORMAT_RAW.get(py)?) {
255235
return Err(CryptographyError::from(
256236
pyo3::exceptions::PyValueError::new_err(
257237
"When using Raw both encoding and format must be Raw",
@@ -270,7 +250,7 @@ pub(crate) fn pkey_public_bytes<'p>(
270250
}
271251

272252
if let Ok(ec) = pkey.ec_key() {
273-
if encoding.is(&types::ENCODING_X962.get(py)?) {
253+
if encoding == Encoding::X962 {
274254
let point_form = if format.is(&types::PUBLIC_FORMAT_UNCOMPRESSED_POINT.get(py)?) {
275255
openssl::ec::PointConversionForm::UNCOMPRESSED
276256
} else if format.is(&types::PUBLIC_FORMAT_COMPRESSED_POINT.get(py)?) {
@@ -305,7 +285,7 @@ pub(crate) fn pkey_public_bytes<'p>(
305285

306286
// OpenSSH + OpenSSH
307287
if openssh_allowed && format.is(&types::PUBLIC_FORMAT_OPENSSH.get(py)?) {
308-
if encoding.is(&types::ENCODING_OPENSSH.get(py)?) {
288+
if encoding == Encoding::OpenSSH {
309289
return Ok(types::SERIALIZE_SSH_PUBLIC_KEY
310290
.get(py)?
311291
.call1((key_obj,))?

0 commit comments

Comments
 (0)