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

Add PKCS7-internal BIO_f_cipher #1836

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

WillChilds-Klein
Copy link
Contributor

@WillChilds-Klein WillChilds-Klein commented Sep 6, 2024

Issues:

Addresses CryptoAlg-2494

Description of changes:

This change introduces a new filter BIO, BIO_f_cipher for use in PR 1816.

The cipher BIO sits in front of a backing BIO, encrypting incoming writes before writing to the backing BIO and decrypting data read from the backing BIO. This implementation is almost an exact copy of OpenSSL's with some functionality removed. We try to change as little of the underlying logic as possible, but rename variables and add comments for clarity.

Call-outs:

  • n/a

Testing:

  • new unit tests. test considerations and flow are documented in-line.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license.

@codecov-commenter
Copy link

codecov-commenter commented Sep 6, 2024

Codecov Report

Attention: Patch coverage is 93.15589% with 18 lines in your changes missing coverage. Please review.

Project coverage is 78.54%. Comparing base (ca8180c) to head (1a2d1dc).

Files with missing lines Patch % Lines
crypto/pkcs7/bio/cipher.c 91.30% 16 Missing ⚠️
crypto/pkcs7/bio/bio_deprecated_test.cc 97.46% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1836      +/-   ##
==========================================
+ Coverage   78.49%   78.54%   +0.04%     
==========================================
  Files         585      587       +2     
  Lines       99630    99893     +263     
  Branches    14254    14289      +35     
==========================================
+ Hits        78206    78459     +253     
- Misses      20789    20797       +8     
- Partials      635      637       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@WillChilds-Klein WillChilds-Klein force-pushed the pkcs7-bio-cipher branch 6 times, most recently from f4627f0 to 4f69b78 Compare September 11, 2024 23:44
@WillChilds-Klein WillChilds-Klein changed the title Add PKCS7-internal cipher BIO Add cipher BIO Sep 13, 2024
@WillChilds-Klein WillChilds-Klein force-pushed the pkcs7-bio-cipher branch 3 times, most recently from 6e5c58d to 94c275e Compare September 13, 2024 15:11
@WillChilds-Klein WillChilds-Klein marked this pull request as ready for review September 13, 2024 15:32
@WillChilds-Klein WillChilds-Klein requested a review from a team as a code owner September 13, 2024 15:32
Copy link
Contributor

@samuel40791765 samuel40791765 left a comment

Choose a reason for hiding this comment

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

Just some preliminary comments, haven't gone through everything yet.

crypto/bio/bio_deprecated_test.cc Outdated Show resolved Hide resolved
include/openssl/bio.h Outdated Show resolved Hide resolved
@WillChilds-Klein WillChilds-Klein force-pushed the pkcs7-bio-cipher branch 2 times, most recently from 6fe97e9 to ea1f09d Compare September 14, 2024 23:49
crypto/bio/cipher.c Outdated Show resolved Hide resolved
crypto/bio/cipher.c Outdated Show resolved Hide resolved
crypto/bio/cipher.c Outdated Show resolved Hide resolved
crypto/bio/cipher.c Outdated Show resolved Hide resolved
crypto/bio/cipher.c Outdated Show resolved Hide resolved
crypto/bio/cipher.c Outdated Show resolved Hide resolved
crypto/bio/cipher.c Outdated Show resolved Hide resolved
crypto/pkcs7/bio/cipher.c Outdated Show resolved Hide resolved
crypto/pkcs7/bio/cipher.c Outdated Show resolved Hide resolved
crypto/pkcs7/bio/cipher.c Outdated Show resolved Hide resolved
@WillChilds-Klein WillChilds-Klein changed the title Add cipher BIO Add PKCS7-internal BIO_f_cipher Sep 29, 2024
crypto/pkcs7/bio/cipher.c Outdated Show resolved Hide resolved
samuel40791765
samuel40791765 previously approved these changes Oct 1, 2024
samuel40791765
samuel40791765 previously approved these changes Oct 2, 2024
crypto/pkcs7/bio/cipher.c Outdated Show resolved Hide resolved
crypto/pkcs7/bio/cipher.c Outdated Show resolved Hide resolved
crypto/pkcs7/bio/cipher.c Outdated Show resolved Hide resolved
crypto/pkcs7/bio/cipher.c Show resolved Hide resolved
crypto/pkcs7/bio/cipher.c Outdated Show resolved Hide resolved
crypto/pkcs7/bio/cipher.c Outdated Show resolved Hide resolved
crypto/pkcs7/bio/cipher.c Outdated Show resolved Hide resolved
crypto/pkcs7/bio/cipher.c Outdated Show resolved Hide resolved
crypto/pkcs7/bio/cipher.c Outdated Show resolved Hide resolved
Comment on lines +121 to +128
if (bytes_read == 0 && BIO_eof(next)) {
// EVP_CipherFinal_ex may write up to one block to our buffer. If that
// happens, continue the loop to process the decrypted block as normal.
ctx->ok = EVP_CipherFinal_ex(ctx->cipher, ctx->buf, &ctx->buf_len);
// We have more bytes to output, go back to top of loop.
if (ctx->buf_len > 0) {
continue;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

To prevent making a second call to EVP_CipherFinal_ex on a subsequent iteration, should it be setting ctx->done = 1 prior to the continue? Then, ctx->done would need to be checked prior to the call to BIO_read(next, ctx->buf, to_read).

Comment on lines +131 to +133
} else if (bytes_read <= 0) {
BIO_copy_next_retry(b);
return bytes_output;
Copy link
Contributor

Choose a reason for hiding this comment

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

The bytes_read <= 0 condition here seems to imply an error in next for which we should also set: ctx->ok = 0?

Our documentation says BIO_read:

returns the number of bytes read, zero on EOF, or a negative number on error

Comment on lines +194 to +196
if (bytes_written <= 0) {
goto out;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we also set ctx->ok = 0 when bytes_written < 0?

int remaining = inl;
const int max_crypt_size =
(int)sizeof(ctx->buf) - EVP_CIPHER_CTX_block_size(ctx->cipher) + 1;
while (remaining > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We should return an error if this is called when ctx->done == 1 (or ctx->ok == 0).

remaining -= to_encrypt;
}
int bytes_written = BIO_write(next, &ctx->buf[ctx->buf_off], ctx->buf_len);
if (bytes_written <= 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

For BIO_write, I think we should distinguish bytes_written == 0 (no bytes written) from bytes_written < 0 (an error). On an error, we might need to set ctx->ok = 0 and ctx->done = 1?

GUARD_PTR(b);
GUARD_PTR(next);
GUARD_PTR(ctx);
while (ctx->buf_len > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this loop instead be conditioned on ctx->buf_len > 0 || ctx->done == 0. It seems possible/likely that ctx->buf_len == 0 holds when BIO_flush is called, in which case this loop never executes (EVP_CipherFinal_ex never gets called), and it returns 1 as-if it succeeded.

  while (ctx->buf_len > 0 || ctx->done == 0) {
    if (ctx->buf_len > 0) {
...
    } else if(ctx->done == 0) {
...
    }
  }

Comment on lines +148 to +152
int bytes_written = BIO_write(next, &ctx->buf[ctx->buf_off], ctx->buf_len);
if (bytes_written <= 0) {
BIO_copy_next_retry(b);
return bytes_written;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

In this case, I think the return value should be 0 b/c the buffer was not fully flushed. (If we return a positive value, the caller may assume EVP_CipherFinal_ex was called and all data was written.

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.

4 participants