-
Notifications
You must be signed in to change notification settings - Fork 574
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
Improve FFI's botan_cipher_update() performance for stream ciphers #3951
Improve FFI's botan_cipher_update() performance for stream ciphers #3951
Conversation
575f79d
to
305552e
Compare
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
51da010
to
0b432d9
Compare
While trying to port the AEAD test down to the existing implementation I found a couple more bugs in the inner loop of
|
0b432d9
to
5b83288
Compare
5b83288
to
d8413fc
Compare
Rebased to master after the underlying pull requests were merged. |
d8413fc
to
8b2f42d
Compare
Force-pushed to add GPG signatures to the commit. I'm back at home with access to a USB-C to USB-A adapter for my YubiKey. 🤦♂️ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. This is a lot cleaner and easier to understand. Really demonstrates the benefits of scoped_cleanup
and the span oriented slicers 👍
Opportunistically uses the ideal update granularity to process bytes and implements the finalization more robustly according to the documented "rules of engagement" of the FFI. Particularly, it improves the handling of a too small output buffer during cipher finalization.
8b2f42d
to
cda3843
Compare
Rebased to master and addressed the review comment. |
For the record: Some quick-and-dirty measurement with the tool @aahajj used initially show promising speedups especially for stream ciphers but also for the other cipher modes. See: #3925 (comment) |
@reneme This broke things for RNP AEAD implementation, as we relied on the previous behaviour where |
This is in response to a downstream user question: https://github.com/randombit/botan/pull/3951\#issuecomment-2136990858
@ni4 Sorry about that! For clarification (and for the record): Also previously, @randombit Perhaps we should mention that gotcha in the changelog?
Yes, it won't ever produce more bytes than provided as input. Unless during finalization to emit the AEAD's authentication tag. I clarified that in the documentation of the function as well: #4088. |
while(in.remaining() >= granularity && out.remaining_capacity() >= expected_output_per_iteration) { | ||
copy_mem(mbuf, in.take(granularity)); | ||
const auto written_bytes = cipher.process(mbuf); | ||
BOTAN_DEBUG_ASSERT(written_bytes == expected_output_per_iteration); | ||
if(written_bytes > 0) { | ||
BOTAN_ASSERT_NOMSG(written_bytes <= granularity); | ||
copy_mem(out.next(written_bytes), std::span(mbuf).first(written_bytes)); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@randombit There's more room for optimization here. We could detect that the user passed the same buffer for input and output and omit the copy to the internal mbuf
in that case.
@reneme We used the update granularity, and I believe that at some point in the past it was like 16 bytes or so. Probably at some point that changed.
Yeah, tag should not be a problem. We just use the same preallocated buffer to encrypt/decrypt (and it is the same for input and output, like you mentioned in the further comment) so it would be unwilling to do allocations during the call. Thanks! |
Pull Request Dependencies
Description
This reworks the implementation of FFI
botan_cipher_update()
, making it comply with the general "rules of engagement" of the FFI. Note that this subtly changes the behavior when finalizing a cipher without providing a big enough output buffer up-front.When the caller failed to provide a big enough output buffer, it now returns
BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE
instead ofBOTAN_FFI_ERROR_INVALID_INPUT
and setsoutput_written
to the required number of bytes. In that case, re-invokingbotan_cipher_update()
with the "final" flag and a sufficiently sized output buffer then obtains the remaining bytes and resets the cipher object.Before, the re-invocation had to happen without setting the final flag again to obtain the same behavior.
@randombit Frankly, I doubt that the existing implementation supported such a usage explicitly. It did feel like an inadvertent hack to me. That's why I changed it to an (IMHO) more logical interface. Nevertheless, there might be an argument to keep the existing behavior, albeit inconsistent with the generic "rules of engagement".
The rework should also significantly improve the performance of stream cipher modes (e.g. CTR-mode) via the FFI by opportunistically leveraging the
ideal_update_granularity()
of the cipher mode. Before, we generally processed data in chunks ofupdate_granularity()
, which is "1 byte" for stream ciphers. That resulted in very poor performance, as described in #3925.TODO: We might look into the internal function
ffi_choose_update_size()
and whether it still makes sense in this new setup. Frankly, I'm lacking the historic context to conclusively judge that.Closes #3925.