Skip to content

This PR adds support for CMS. #5251

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

Closed
wants to merge 1 commit into from
Closed

This PR adds support for CMS. #5251

wants to merge 1 commit into from

Conversation

elear
Copy link

@elear elear commented Mar 10, 2020

Hi everyone,

This is my first PR for this project. It contains support for CMS, which is RFC 5652. The code borrows heavily from earlier work, because mostly speaking CMS is an incremental upgrade of PKCS#7. The notable changes are these:

  • PKCS7 struct to CMS_ContentInfo
  • Some internalizing of tests (e.g, they're shoved down to OpenSSL)
  • change in the field size for flags

CMS is not present in all versions of OpenSSL. Notably Apple is doing something funky these days with LibreSSL. No idea how they are compiling PHP, but if a dependency is required, it is not yet in this PR.

@elear
Copy link
Author

elear commented Mar 11, 2020

I am thinking of adding some additional capability to express DER/PEM/CMS output in openssl_cms_sign. This provides a bit more flexibility beyond email of the CMS routines.

@nikic nikic requested a review from bukka March 11, 2020 20:16
@elear
Copy link
Author

elear commented Mar 13, 2020

Please note a additional commits that provide for use and testing of different encodings.

@bukka
Copy link
Member

bukka commented Mar 15, 2020

Just had a quick look and essentially it is pkcs7 moved to CMS which I think is probably a good way how to get it finally in. It might be worth to merge some common parts together. It would be also good to have a way to get CMS_ContentInfo to user so further operation can be performed on it but I think that's way out of scope for this.

I need to do a bit more thorough review but could you please clean it up a little bit. Mainly those parts:

  • When it's exactly the same code in pkcs7 and CMS and can be separated from, it would be good to move it to the function so there is some code re-use.
  • Variables are still named for pkcs7 so might be good to use names for CMS.
  • CS doesn't look right (e.g. switches and some conditions as well as enum defs and pointers) - we use just tabs and spaces between =. Although it's not that consistent in openssl.c, it would be good if new code tries to follow it.

@elear
Copy link
Author

elear commented Mar 15, 2020

@bukka Thank you very much for your review. I'll take a look at code reuse. I think there should be a bit, but some logic has changed. For instance CMS_ContentInfo really is an opaque struct. If you like, what I can do is expose the various cms_get0 functions that have same semantic value as poking around the PKCS7 struct. Wilco on the rest.

@elear
Copy link
Author

elear commented Mar 16, 2020

Hi,

I've looked at factoring out code to create static functions. At the end of the day, it's not worth it for several reasons:

  1. The type being operated upon is changed throughout- (PKCS7->CMS_ContentInfo)
  2. Some of the logic has indeed changed. PKCS7 is not an opaque struct whereas CMS_ContentInfo is.
  3. A few of the calling interfaces have changed. This includes the width of number of flag fields.
  4. I am introducing substantial new functionality with CMS. I am not doing this with PKCS#7 because I would rather first see that the code is found to be generally stable with CMS and then backport the capability to PKCS#7.

In the last commit I did my best to bring the coding style in line with what you would expect. I welcome further edits on top if you spot any issues.

@elear
Copy link
Author

elear commented Mar 30, 2020

@bukka any chance to move this one along?

@bukka
Copy link
Member

bukka commented Apr 5, 2020

Added some comments but looks mostly good.

@elear
Copy link
Author

elear commented Apr 6, 2020

Not yet safe to merge. Look for another commit shortly.

@elear
Copy link
Author

elear commented Apr 7, 2020

@bukka Please have one more look. Thanks!

@cmb69
Copy link
Member

cmb69 commented May 31, 2020

Let me XFAIL the test for Windows for now.

But it does not fail, does it?

@elear
Copy link
Author

elear commented May 31, 2020

It does on Appveyor for some reason - sometimes. Serious head scratching going on here.

@elear
Copy link
Author

elear commented May 31, 2020

@cmb69 ok, I see. I hadn't realized that an XFAIL would warn on a pass. Suggestions?

@cmb69
Copy link
Member

cmb69 commented May 31, 2020

Well, is it possible that the issue is due to text mode translation (LF <-> CRLF)?

@elear
Copy link
Author

elear commented May 31, 2020

I don't think so. When it blows up, what I am seeing is garbage out of CMS_encrypt: an invalid der file is being created, and no error is produced.

@elear
Copy link
Author

elear commented May 31, 2020

Also- I think I did try setting CMS_BINARY and the same issue occurred. Again, it only seems to happen periodically with Appveyor.

@cmb69
Copy link
Member

cmb69 commented May 31, 2020

When it blows up, what I am seeing is garbage out of CMS_encrypt: an invalid der file is being created, and no error is produced.

Is it really CMS_encrypt producing garbage, or is there a chance that another test run in parallel interferes (maybe due to an issue with tmpname on Windows, or whatever)?

Anyhow, I would suggest to dynamically mark the test as "xfail" on Windows (you can just replace the "skip" argument of the print statement with "xfail"; ideally add some reason for why it is marked as xfail), so that it is run (and as such produces a diff), and also because the test ought to pass on Windows. The warning, in case the test succeedes, isn't a real issue.

@bukka
Copy link
Member

bukka commented May 31, 2020

Hmm that's strange. Might be worth to add some debugging output (e.g. openssl errors) or maybe hack it a bit so more info is visible in AppVeyor.

If skip or xfail is added, it should be clear what the reason is for that so we are sure that it's not a real issue.

@elear
Copy link
Author

elear commented Jun 1, 2020

ok, i'm going to trim down the test to make sure there's no extraneous gunk

@elear
Copy link
Author

elear commented Jun 1, 2020

Just to follow up, the test is currently passing, so it is possible there was something going on with tempnam() on Windows. I propose to just do a bunch of git push -fs during the week to see if the problem recurs.

@elear
Copy link
Author

elear commented Jun 1, 2020

FYI I pounded the CI servers all day today and didn't run into any CMS-related failures.

@elear
Copy link
Author

elear commented Jun 2, 2020

@bukka @cmb69 Ok, I'm comfortable with this code getting merged.

@cmb69
Copy link
Member

cmb69 commented Jun 2, 2020

Fine, thanks for checking the AppVeyor issue!

I'm not sure what to do with the RFC. I don't think this PR needs an RFC.

@elear
Copy link
Author

elear commented Jun 2, 2020

@cmb69 I am happy to retire the RFC of course if the maintainers would just like to merge. I would like to retain it in the archives, because it will help with documentation.

@cmb69
Copy link
Member

cmb69 commented Jun 2, 2020

Yes, the RFC is definitely very useful for the documentation, and UPGRADING could just get a link to the RFC instead of lengthy explanation.

*/
function openssl_cms_decrypt(string $infilename, string $outfilename, $recipcert, $recipkey, int $encoding = OPENSSL_ENCODING_SMIME): bool {}

function openssl_cms_read(string $infilename, &$certs): bool {}
Copy link
Contributor

Choose a reason for hiding this comment

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

Any better type possible for $certs?

Copy link
Member

Choose a reason for hiding this comment

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

For references, the type is only checked on input, but this is an out parameter. So no, no better type possible for now.

Copy link
Author

Choose a reason for hiding this comment

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

That and this is directly analogous to the pkcs7 call.

is an update to PKCS7.  The functions are analogous BUT NOT IDENTICAL
to openssl_pkcs7*.  In particular, support for different encodings
(PEM, DER, SMIME) is now available.
@elear
Copy link
Author

elear commented Jun 7, 2020

@bukka good to go?

@bukka
Copy link
Member

bukka commented Jun 7, 2020

@elear Yes just merged as 367c55f (sorry for small grammar mistake that I did in the slightly changed git commit message: should be it adds - hopefully no one reads commit messages :) ).

Thanks for this very nice addition to the OpenSSL ext!

@bukka bukka closed this Jun 7, 2020
@bukka
Copy link
Member

bukka commented Jun 7, 2020

Oh wrong commit linked. The 367c55f is for NEWS and UPGRADING changes that I did.

This is the commit with the actual change: 8583b8a

@carusogabriel carusogabriel removed the RFC label Jun 7, 2020
@carusogabriel carusogabriel added this to the PHP 8.0 milestone Jun 7, 2020
@elear
Copy link
Author

elear commented Jun 8, 2020

Thank you all!

@nikic
Copy link
Member

nikic commented Jun 8, 2020

I've applied a small memory leak fix in 57e17e5.

jrfnl added a commit to PHPCompatibility/PHPCompatibility that referenced this pull request Jul 19, 2020
> - Added openssl_cms_encrypt() encrypts the message in the file with the
>    certificates and outputs the result to the supplied file.
> - Added openssl_cms_decrypt() that decrypts the S/MIME message in the file
>    and outputs the results to the supplied file.
> - Added openssl_cms_read() that exports the CMS file to an array of PEM
>    certificates.
> - Added openssl_cms_sign() that signs the MIME message in the file with
>    a cert and key and output the result to the supplied file.
> - Added openssl_cms_verify() that verifies that the data block is intact,
>   the signer is who they say they are, and returns the certs of the signers.

Refs:
* https://wiki.php.net/rfc/add-cms-support
* https://github.com/php/php-src/blob/c0172aa2bdb9ea223c8491bdb300995b93a857a0/UPGRADING#L727-L736
* php/php-src#5251
* php/php-src@8583b8a

Includes unit tests.
jrfnl added a commit to PHPCompatibility/PHPCompatibility that referenced this pull request Jul 19, 2020
> Added Cryptographic Message Syntax (CMS) (RFC 5652) support composed of
> functions for encryption, decryption, signing, verifying and reading. The
> API is similar to the API for PKCS #7 functions with an addition of new
> encoding constants: OPENSSL_ENCODING_DER, OPENSSL_ENCODING_SMIME and
> OPENSSL_ENCODING_PEM.

And from the RFC:
>  The following analogs to PKCS#7 are also added:
>  OPENSSL_CMS_DETACHED
>  OPENSSL_CMS_TEXT
>  OPENSSL_CMS_NOINTERN
>  OPENSSL_CMS_NOVERIFY
>  OPENSSL_CMS_NOCERTS
>  OPENSSL_CMS_NOATTR
>  OPENSSL_CMS_BINARY
>  OPENSSL_CMS_NOSIGS

Refs:
* https://wiki.php.net/rfc/add-cms-support
* https://github.com/php/php-src/blob/c0172aa2bdb9ea223c8491bdb300995b93a857a0/UPGRADING#L603-L608
* php/php-src#5251
* php/php-src@8583b8a

Includes unit test.
@nikic
Copy link
Member

nikic commented Aug 14, 2020

For both openssl_pkcs7_decrypt and openssl_cms_decrypt, what is the reason for $recipkey to be optional? The current logic tries to take the private key from the recipcert if recipkey is not given, but how can the certificate contain a private key?

It doesn't look like there's test coverage for such usage.

@elear
Copy link
Author

elear commented Aug 14, 2020

Hi @nikic I think the magic is in php_openssl_pkey_from_zval() which does indeed try to go hunting for a private key in a cert. I haven't chased this down for the cms routine, but a test case would not be out of order.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants