From 45c17fba48bb83695aeaf1c7cc9ccd16435d23c5 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Wed, 24 Jan 2024 13:54:22 -0800 Subject: [PATCH 1/3] allow SPKI RSA keys to be parsed even if they have an incorrect delimiter This allows RSA SPKI keys (typically delimited with PUBLIC KEY) to be parsed even if they are using the RSA PUBLIC KEY delimiter. --- docs/development/test-vectors.rst | 3 +++ src/rust/src/backend/keys.rs | 10 +++++++++- tests/hazmat/primitives/test_serialization.py | 3 +++ .../rsa_wrong_delimiter_public_key.pem | 9 +++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 vectors/cryptography_vectors/asymmetric/PEM_Serialization/rsa_wrong_delimiter_public_key.pem diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst index 1255688840f3..b73d2f1d3c8c 100644 --- a/docs/development/test-vectors.rst +++ b/docs/development/test-vectors.rst @@ -78,6 +78,9 @@ Custom asymmetric vectors * ``asymmetric/PEM_Serialization/rsa_public_key.pem`` and ``asymmetric/DER_Serialization/rsa_public_key.der``- Contains an RSA 2048 bit public generated using OpenSSL from ``rsa_private_key.pem``. +* ``asymmetric/PEM_Serialization/rsa_wrong_delimiter_public_key.pem`` - Contains + an RSA 2048 bit public key generated from ``rsa_private_key.pem``, but with + the wrong PEM delimiter (``RSA PUBLIC KEY`` when it should be ``PUBLIC KEY``). * ``asymmetric/PEM_Serialization/dsa_4096.pem`` - Contains a 4096-bit DSA private key generated using OpenSSL. * ``asymmetric/PEM_Serialization/dsaparam.pem`` - Contains 2048-bit DSA diff --git a/src/rust/src/backend/keys.rs b/src/rust/src/backend/keys.rs index bd3e8eb28e3b..46d7e3667233 100644 --- a/src/rust/src/backend/keys.rs +++ b/src/rust/src/backend/keys.rs @@ -165,7 +165,15 @@ fn load_pem_public_key( let _ = backend; let p = pem::parse(data.as_bytes())?; let pkey = match p.tag() { - "RSA PUBLIC KEY" => cryptography_key_parsing::rsa::parse_pkcs1_public_key(p.contents())?, + "RSA PUBLIC KEY" => { + // We try to parse it as a PKCS1 first since that's the PEM delimiter, and if + // that fails we try to parse it as an SPKI. This is to match the permissiveness + // of OpenSSL, which doesn't care about the delimiter. + match cryptography_key_parsing::rsa::parse_pkcs1_public_key(p.contents()) { + Ok(pkey) => pkey, + Err(_) => cryptography_key_parsing::spki::parse_public_key(p.contents())?, + } + }, "PUBLIC KEY" => cryptography_key_parsing::spki::parse_public_key(p.contents())?, _ => return Err(CryptographyError::from(pem::PemError::MalformedFraming)), }; diff --git a/tests/hazmat/primitives/test_serialization.py b/tests/hazmat/primitives/test_serialization.py index 58693a4912d2..14fe123f7343 100644 --- a/tests/hazmat/primitives/test_serialization.py +++ b/tests/hazmat/primitives/test_serialization.py @@ -506,6 +506,9 @@ def test_load_pem_ec_private_key(self, key_path, password, backend): "asymmetric", "PEM_Serialization", "rsa_public_key.pem" ), os.path.join("asymmetric", "public", "PKCS1", "rsa.pub.pem"), + os.path.join( + "asymmetric", "PEM_Serialization", "rsa_wrong_delimiter_public_key.pem" + ), ], ) def test_load_pem_rsa_public_key(self, key_file, backend): diff --git a/vectors/cryptography_vectors/asymmetric/PEM_Serialization/rsa_wrong_delimiter_public_key.pem b/vectors/cryptography_vectors/asymmetric/PEM_Serialization/rsa_wrong_delimiter_public_key.pem new file mode 100644 index 000000000000..78053b4e6ed9 --- /dev/null +++ b/vectors/cryptography_vectors/asymmetric/PEM_Serialization/rsa_wrong_delimiter_public_key.pem @@ -0,0 +1,9 @@ +-----BEGIN RSA PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnR4AZ+tgWYql+S3MaTQ6 +zeIO1fKzFIoau9Q0zGuv/1oCAewXwxeDSSxw+/Z3GL1NpuuS9CpbR5EQ3d71bD0v +0G+Sf+mShSl0oljG7YqnNSPzKl+EQ3/KE+eEButcwas6KGof2BA4bFNCw/fPbuhk +u/d8sIIEgdzBMiGRMdW33uci3rsdOenMZQA7uWsM/q/pu85YLAVOxq6wlUCzP4FM +Tw/RKzayrPkn3Jfbqcy1aM2HDlFVx24vaN+RRbPSnVoQbo5EQYkUMXE8WmadSyHl +pXGRnWsJSV9AdGyDrbU+6tcFwcIwnW22jb/OJy8swHdqKGkuR1kQ0XqokK1yGKFZ +8wIDAQAB +-----END RSA PUBLIC KEY----- From 46336076d8b82605877922314312648756b93a25 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Wed, 24 Jan 2024 14:02:43 -0800 Subject: [PATCH 2/3] formatting --- src/rust/src/backend/keys.rs | 2 +- tests/hazmat/primitives/test_serialization.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rust/src/backend/keys.rs b/src/rust/src/backend/keys.rs index 46d7e3667233..dfb3bea11e9d 100644 --- a/src/rust/src/backend/keys.rs +++ b/src/rust/src/backend/keys.rs @@ -173,7 +173,7 @@ fn load_pem_public_key( Ok(pkey) => pkey, Err(_) => cryptography_key_parsing::spki::parse_public_key(p.contents())?, } - }, + } "PUBLIC KEY" => cryptography_key_parsing::spki::parse_public_key(p.contents())?, _ => return Err(CryptographyError::from(pem::PemError::MalformedFraming)), }; diff --git a/tests/hazmat/primitives/test_serialization.py b/tests/hazmat/primitives/test_serialization.py index 14fe123f7343..6652e8657c9c 100644 --- a/tests/hazmat/primitives/test_serialization.py +++ b/tests/hazmat/primitives/test_serialization.py @@ -507,7 +507,9 @@ def test_load_pem_ec_private_key(self, key_path, password, backend): ), os.path.join("asymmetric", "public", "PKCS1", "rsa.pub.pem"), os.path.join( - "asymmetric", "PEM_Serialization", "rsa_wrong_delimiter_public_key.pem" + "asymmetric", + "PEM_Serialization", + "rsa_wrong_delimiter_public_key.pem", ), ], ) From 8ca17c640ab5d943b892bb2e52f3dffc8d2aedcc Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Wed, 24 Jan 2024 17:45:30 -0800 Subject: [PATCH 3/3] use original error if nothing parses, don't let it parse non-RSA --- docs/development/test-vectors.rst | 3 +++ src/rust/src/backend/keys.rs | 13 ++++++++++++- tests/hazmat/primitives/test_serialization.py | 11 +++++++++++ .../ec_public_key_rsa_delimiter.pem | 4 ++++ 4 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 vectors/cryptography_vectors/asymmetric/PEM_Serialization/ec_public_key_rsa_delimiter.pem diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst index b73d2f1d3c8c..0b1f238ffaa2 100644 --- a/docs/development/test-vectors.rst +++ b/docs/development/test-vectors.rst @@ -72,6 +72,9 @@ Custom asymmetric vectors * ``asymmetric/PEM_Serialization/ec_public_key.pem`` and ``asymmetric/DER_Serialization/ec_public_key.der``- Contains the public key corresponding to ``ec_private_key.pem``, generated using OpenSSL. +* ``asymmetric/PEM_Serialization/ec_public_key_rsa_delimiter.pem`` - Contains + the public key corresponding to ``ec_private_key.pem``, but with the wrong PEM + delimiter (``RSA PUBLIC KEY`` when it should be ``PUBLIC KEY``). * ``asymmetric/PEM_Serialization/rsa_private_key.pem`` - Contains an RSA 2048 bit key generated using OpenSSL, protected by the secret "123456" with DES3 encryption. diff --git a/src/rust/src/backend/keys.rs b/src/rust/src/backend/keys.rs index dfb3bea11e9d..ecdff5db6dcb 100644 --- a/src/rust/src/backend/keys.rs +++ b/src/rust/src/backend/keys.rs @@ -171,7 +171,18 @@ fn load_pem_public_key( // of OpenSSL, which doesn't care about the delimiter. match cryptography_key_parsing::rsa::parse_pkcs1_public_key(p.contents()) { Ok(pkey) => pkey, - Err(_) => cryptography_key_parsing::spki::parse_public_key(p.contents())?, + Err(err) => { + let pkey = cryptography_key_parsing::spki::parse_public_key(p.contents()) + .map_err(|_| err)?; + if pkey.id() != openssl::pkey::Id::RSA { + return Err(CryptographyError::from( + pyo3::exceptions::PyValueError::new_err( + "Incorrect PEM delimiter for key type.", + ), + )); + } + pkey + } } } "PUBLIC KEY" => cryptography_key_parsing::spki::parse_public_key(p.contents())?, diff --git a/tests/hazmat/primitives/test_serialization.py b/tests/hazmat/primitives/test_serialization.py index 6652e8657c9c..51fcc3563d8a 100644 --- a/tests/hazmat/primitives/test_serialization.py +++ b/tests/hazmat/primitives/test_serialization.py @@ -525,6 +525,17 @@ def test_load_pem_rsa_public_key(self, key_file, backend): numbers = key.public_numbers() assert numbers.e == 65537 + def test_load_pem_public_fails_with_ec_key_with_rsa_delimiter(self): + with pytest.raises(ValueError): + load_vectors_from_file( + os.path.join( + "asymmetric", + "PEM_Serialization", + "ec_public_key_rsa_delimiter.pem", + ), + lambda pemfile: load_pem_public_key(pemfile.read().encode()), + ) + def test_load_priv_key_with_public_key_api_fails( self, rsa_key_2048, backend ): diff --git a/vectors/cryptography_vectors/asymmetric/PEM_Serialization/ec_public_key_rsa_delimiter.pem b/vectors/cryptography_vectors/asymmetric/PEM_Serialization/ec_public_key_rsa_delimiter.pem new file mode 100644 index 000000000000..565ece176bf5 --- /dev/null +++ b/vectors/cryptography_vectors/asymmetric/PEM_Serialization/ec_public_key_rsa_delimiter.pem @@ -0,0 +1,4 @@ +-----BEGIN RSA PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJLzzbuz2tRnLFlOL+6bTX6giVavA +sc6NDFFT0IMCd2ibTTNUDDkFGsgq0cH5JYPg/6xUlMBFKrWYe3yQ4has9w== +-----END RSA PUBLIC KEY-----