Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ASN.1 issues in PKCS#7 and S/MIME signing #10373

Merged
merged 5 commits into from
Feb 20, 2024

Conversation

facutuesca
Copy link
Contributor

@facutuesca facutuesca commented Feb 9, 2024

This PR fixes two issues with the ASN.1 generated when signing using PKCS#7:

First issue

The current implementation defines the SMIMECapabilities attribute so that its value is a SEQUENCE of all the algorithm OIDs that are supported. This is what the ASN.1 currently looks like for a signed message using cryptography:

  U.P.SEQUENCE {
     U.P.OBJECTIDENTIFIER 1.2.840.113549.1.9.15 (S/MIME capabilities)
     U.P.SET {
        U.P.SEQUENCE {
           U.P.OBJECTIDENTIFIER 2.16.840.1.101.3.4.1.42 (aes256-CBC-PAD)
           U.P.OBJECTIDENTIFIER 2.16.840.1.101.3.4.1.22 (aes192-CBC-PAD)
           U.P.OBJECTIDENTIFIER 2.16.840.1.101.3.4.1.2 (aes128-CBC-PAD)
        }
     }
  }

However, the S/MIME v3 spec (RFC 2633) specifies that each algorithm should be specified in its own SEQUENCE:

SMIMECapabilities ::= SEQUENCE OF SMIMECapability

SMIMECapability ::= SEQUENCE {
   capabilityID OBJECT IDENTIFIER,
   parameters ANY DEFINED BY capabilityID OPTIONAL }

(RFC 2633, Appendix A)

The spec matches what OpenSSL outputs (using openssl smime -sign):

  U.P.SEQUENCE {
     U.P.OBJECTIDENTIFIER 1.2.840.113549.1.9.15 (S/MIME capabilities)
     U.P.SET {
        U.P.SEQUENCE {
           U.P.SEQUENCE {
              U.P.OBJECTIDENTIFIER 2.16.840.1.101.3.4.1.42 (aes256-CBC-PAD)
           }
           U.P.SEQUENCE {
              U.P.OBJECTIDENTIFIER 2.16.840.1.101.3.4.1.22 (aes192-CBC-PAD)
           }
           U.P.SEQUENCE {
              U.P.OBJECTIDENTIFIER 2.16.840.1.101.3.4.1.2 (aes128-CBC-PAD)
           }
           U.P.SEQUENCE {
              U.P.OBJECTIDENTIFIER 1.2.840.113549.3.7 (id_des_EDE3_CBC)
           }
          .....

This PR changes the implementation so that each algorithm is inside its own SEQUENCE. After the changes in this PR, the ASN.1 output looks like:

  U.P.SEQUENCE {
     U.P.OBJECTIDENTIFIER 1.2.840.113549.1.9.15 (S/MIME capabilities)
     U.P.SET {
        U.P.SEQUENCE {
           U.P.SEQUENCE {
              U.P.OBJECTIDENTIFIER 2.16.840.1.101.3.4.1.42 (aes256-CBC-PAD)
           }
           U.P.SEQUENCE {
              U.P.OBJECTIDENTIFIER 2.16.840.1.101.3.4.1.22 (aes192-CBC-PAD)
           }
           U.P.SEQUENCE {
              U.P.OBJECTIDENTIFIER 2.16.840.1.101.3.4.1.2 (aes128-CBC-PAD)
           }
        }
     }
  }

Second issue

There is an issue with the RSA OID used for signing PKCS#7/SMIME, in the SignatureAlgorithmIdentifier field.

The current implementation computes the algorithm identifier used in the digest_encryption_algorithm PKCS#7 field
(or SignatureAlgorithmIdentifier in S/MIME) based on both the algorithm used to sign (e.g. RSA) and the digest algorithm (e.g. SHA512).

This is correct for ECDSA signatures, where the OIDs used include the digest algorithm (e.g: ecdsa-with-SHA512). However, due to historical reasons, when signing with RSA the OID specified should be the one
corresponding to just RSA ("1.2.840.113549.1.1.1" rsaEncryption), rather than OIDs which also include the digest algorithm (such as "1.2.840.113549.1.1.13" sha512WithRSAEncryption).

This means that the logic to compute the algorithm identifier is the same except when signing with RSA, in which case the OID will always be rsaEncryption. This is consistent with the OpenSSL implementation, and the RFCs that define PKCS#7 and S/MIME.

See RFC 3851 (section 2.2), and RFC 3370 (section 3.2) for more details.

@alex
Copy link
Member

alex commented Feb 9, 2024

Haven't re-reviewed the RFC, but we should add a test for this.

@alex alex added this to the Forty Third Release milestone Feb 10, 2024
@facutuesca facutuesca changed the title Fix ASN.1 for S/MIME capabilities. Fix ASN.1 issues in PKCS#7 and S/MIME signing Feb 12, 2024
@facutuesca facutuesca force-pushed the smime-capabilities-fix branch 4 times, most recently from 2755b62 to 1f797f8 Compare February 12, 2024 21:22
@facutuesca facutuesca marked this pull request as ready for review February 12, 2024 21:32
if key_type == x509::sign::KeyType::Rsa && !has_pss_padding {
Ok(common::AlgorithmIdentifier {
oid: asn1::DefinedByMarker::marker(),
params: common::AlgorithmParameters::Rsa(None),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
params: common::AlgorithmParameters::Rsa(None),
params: common::AlgorithmParameters::Rsa(Some(())),

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

rsa_padding: &'p pyo3::PyAny,
) -> pyo3::PyResult<common::AlgorithmIdentifier<'static>> {
let key_type = x509::sign::identify_key_type(py, private_key)?;
let has_pss_padding = !rsa_padding.is_none() && rsa_padding.is_instance(types::PSS.get(py)?)?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let has_pss_padding = !rsa_padding.is_none() && rsa_padding.is_instance(types::PSS.get(py)?)?;
let has_pss_padding = rsa_padding.is_instance(types::PSS.get(py)?)?;

I don't think the is_none() check is required? None isn't an instance of PSS :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I got that code from

// If this is RSA-PSS we need to compute the signature algorithm from the
// parameters provided in rsa_padding.
if !rsa_padding.is_none() && rsa_padding.is_instance(types::PSS.get(py)?)? {
. Should I remove it from there as well?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably that should be fixed, but no need to do it in this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

The current implementation defines the SMIMECapabilities attribute
so that its value is a SEQUENCE of all the algorithm OIDs that are
supported.
However, the S/MIME v3 spec (RFC 2633) specifies that each algorithm
should be specified in its own SEQUENCE:

SMIMECapabilities ::= SEQUENCE OF SMIMECapability

SMIMECapability ::= SEQUENCE {
   capabilityID OBJECT IDENTIFIER,
   parameters ANY DEFINED BY capabilityID OPTIONAL }

(RFC 2633, Appendix A)

This commit changes the implementation so that each algorithm
is inside its own SEQUENCE. This also matches the OpenSSL
implementation.
The current implementation computes the algorithm identifier used
in the `digest_encryption_algorithm` PKCS#7 field
(or `SignatureAlgorithmIdentifier` in S/MIME) based on both the
algorithm used to sign (e.g. RSA) and the digest algorithm (e.g. SHA512).

This is correct for ECDSA signatures, where the OIDs used include the
digest algorithm (e.g: ecdsa-with-SHA512). However, due to historical
reasons, when signing with RSA the OID specified should be the one
corresponding to just RSA ("1.2.840.113549.1.1.1" rsaEncryption),
rather than OIDs which also include the digest algorithm (such as
"1.2.840.113549.1.1.13", sha512WithRSAEncryption).

This means that the logic to compute the algorithm identifier is the
same except when signing with RSA, in which case the OID will always
be `rsaEncryption`. This is consistent with the OpenSSL implementation,
and the RFCs that define PKCS#7 and S/MIME.

See RFC 3851 (section 2.2), and RFC 3370 (section 3.2) for more details.
@facutuesca
Copy link
Contributor Author

@alex I also added an item to the CHANGELOG

@facutuesca facutuesca requested a review from alex February 20, 2024 06:20
@alex alex merged commit 8ef3b38 into pyca:main Feb 20, 2024
57 checks passed
alex pushed a commit to alex/cryptography that referenced this pull request Feb 20, 2024
* Fix ASN.1 for S/MIME capabilities.

The current implementation defines the SMIMECapabilities attribute
so that its value is a SEQUENCE of all the algorithm OIDs that are
supported.
However, the S/MIME v3 spec (RFC 2633) specifies that each algorithm
should be specified in its own SEQUENCE:

SMIMECapabilities ::= SEQUENCE OF SMIMECapability

SMIMECapability ::= SEQUENCE {
   capabilityID OBJECT IDENTIFIER,
   parameters ANY DEFINED BY capabilityID OPTIONAL }

(RFC 2633, Appendix A)

This commit changes the implementation so that each algorithm
is inside its own SEQUENCE. This also matches the OpenSSL
implementation.

* Fix the RSA OID used for signing PKCS#7/SMIME

The current implementation computes the algorithm identifier used
in the `digest_encryption_algorithm` PKCS#7 field
(or `SignatureAlgorithmIdentifier` in S/MIME) based on both the
algorithm used to sign (e.g. RSA) and the digest algorithm (e.g. SHA512).

This is correct for ECDSA signatures, where the OIDs used include the
digest algorithm (e.g: ecdsa-with-SHA512). However, due to historical
reasons, when signing with RSA the OID specified should be the one
corresponding to just RSA ("1.2.840.113549.1.1.1" rsaEncryption),
rather than OIDs which also include the digest algorithm (such as
"1.2.840.113549.1.1.13", sha512WithRSAEncryption).

This means that the logic to compute the algorithm identifier is the
same except when signing with RSA, in which case the OID will always
be `rsaEncryption`. This is consistent with the OpenSSL implementation,
and the RFCs that define PKCS#7 and S/MIME.

See RFC 3851 (section 2.2), and RFC 3370 (section 3.2) for more details.

* Add tests for the changes in PKCS7 signing

* PKCS7 fixes from code review

* Update CHANGELOG
reaperhulk pushed a commit that referenced this pull request Feb 21, 2024
* Fix ASN.1 for S/MIME capabilities.

The current implementation defines the SMIMECapabilities attribute
so that its value is a SEQUENCE of all the algorithm OIDs that are
supported.
However, the S/MIME v3 spec (RFC 2633) specifies that each algorithm
should be specified in its own SEQUENCE:

SMIMECapabilities ::= SEQUENCE OF SMIMECapability

SMIMECapability ::= SEQUENCE {
   capabilityID OBJECT IDENTIFIER,
   parameters ANY DEFINED BY capabilityID OPTIONAL }

(RFC 2633, Appendix A)

This commit changes the implementation so that each algorithm
is inside its own SEQUENCE. This also matches the OpenSSL
implementation.

* Fix the RSA OID used for signing PKCS#7/SMIME

The current implementation computes the algorithm identifier used
in the `digest_encryption_algorithm` PKCS#7 field
(or `SignatureAlgorithmIdentifier` in S/MIME) based on both the
algorithm used to sign (e.g. RSA) and the digest algorithm (e.g. SHA512).

This is correct for ECDSA signatures, where the OIDs used include the
digest algorithm (e.g: ecdsa-with-SHA512). However, due to historical
reasons, when signing with RSA the OID specified should be the one
corresponding to just RSA ("1.2.840.113549.1.1.1" rsaEncryption),
rather than OIDs which also include the digest algorithm (such as
"1.2.840.113549.1.1.13", sha512WithRSAEncryption).

This means that the logic to compute the algorithm identifier is the
same except when signing with RSA, in which case the OID will always
be `rsaEncryption`. This is consistent with the OpenSSL implementation,
and the RFCs that define PKCS#7 and S/MIME.

See RFC 3851 (section 2.2), and RFC 3370 (section 3.2) for more details.

* Add tests for the changes in PKCS7 signing

* PKCS7 fixes from code review

* Update CHANGELOG

Co-authored-by: Facundo Tuesca <[email protected]>
@facutuesca facutuesca deleted the smime-capabilities-fix branch February 21, 2024 07:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants