-
Notifications
You must be signed in to change notification settings - Fork 23
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
Adding Aes128Gcm support via rust-ece
crate delegation
#23
Conversation
Since the ece work is now handled by another crate
In order to avoid having to convert it and cloning the underlying Strings
ECE work delegated to rust-ece crate
What? Somebody else wrote an ece crate? Oh nice! The amount of tears and pain to write it with |
Please poke me if you're done and I'm not answering soon enough. A new sprint starting and I'm in the middle of the long meetings. Also, please update the README and see that the rustdocs reflect the current state of the crate. |
For breaking changes, we go to v0.8 anyhow because of Tokio 1 and (if somebody does it) a possible support for async-std runtime. So if you need to break any apis, now's the time. |
Great news, my |
Ok @pimeys , responding here cause the two concerns you presented above are actually linked. I first responded with some suggestions on how to fix the problem then dove into it and found a far simpler approach that is just a bit suboptimal in regard to code duplication between the The Anyway, I'm going to submit a new commit for both those issues quite soon ;) EDIT: For reference, I've just suggested these changes to the |
Great. Probably this kind of optimization doesn't matter for most users, but before we went bankrupt with the company I was writing this crate for, we used it to send thousands and thousands notifications per second. All extra allocations start to show up, and, well, it's very cool if you can make it as fast as possible. :D I can accept this of course as-is too, if there's no way around some of the added computation. |
Hello @pimeys Just to let you know that I wrote a version that avoids the allocation/hashing, but in order to fix this properly, I've opened a new pull request on the There would not be any performance difference, just a question of using the functions provided by the Anyway, there would be no breaking change and moving from that implementation to the one in the (I still need to run some real-world tests before merging) |
Hi again @pimeys ! So I have good news and bad news. Good news: I tested the Aes128Gcm back-end with real world notifications and it works! Bad news: for it to work, I had to downgrade Would you be familiar with the Tokio problem? Does the problem occur on your end with the current |
I'm trying to debug this on your branch, but I don't really have proper subscription info or anything, so I go with the following: {
"endpoint": "asdf",
"keys": {
"p256dh": "asdf",
"auth": "asdf"
}
} I get:
which means that the Tokio 1.1 and Hyper 0.14 work just as they should. The error you get is that you try to run something that expects Tokio 1 in a Tokio 0.2 or 0.3 reactor. That's unfortunate how Tokio forces you to use their machinery and exact versions. Have you tried |
Hey, thanks for the quick reply. I was investigating and I came to the same conclusion. My example uses actix and it seems that the current actix_web version has a dependency that itself uses tokio 0.2. I'll need to try it with the beta version of actix. |
This is the reason I'd like this crate, a2 and fcm all to be independent on runtimes. Maybe refactoring it using https://docs.rs/async-h1/2.3.1/async_h1/ would already make it work much better with every possible system... |
Btw. I started hacking away trying to make this crate runtime-independent. This is not really in scope of this ticket, but I'm in Matrix: @oh_lawd:nauk.io if you want to chat more about the general direction of rust-web-push :) |
I've tested against
And I am proud to announce that it works! I think this is ready for merging or at least a review ;) |
@@ -14,27 +14,30 @@ pub struct WebPushClient { | |||
impl WebPushClient { | |||
pub fn new() -> WebPushClient { | |||
let mut builder = Client::builder(); | |||
builder.keep_alive(true); | |||
builder.pool_max_idle_per_host(std::usize::MAX); |
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.
Are you sure about this? This is a lot of connections...
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.
This was because keep_alive
was deprecated, and the deprecation message was suggesting using pool_max_idle_per_host
(which it does under the hood). usize::MAX
is the default value and means "no limit".
If you look at the code for the keep_alive
function, setting it to true
does exactly that. But if you want to introduce a hard limit, now is the time 😉!
In any case since this is hyper
-specific, it is removed in your other pull request.
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.
Ah, no, this is interesting. I'm just surprised you'd have a limit of 18446744073709551615
connections. You'd first just hit your system ulimit
, which is about 1024 or something similar, depending on your setup. I don't think you need to set this anymore, but please check from Hyper what is the correct way of having keep-alive set for http1.
Btw, hyper will be gone if we merge my PR about being runtime-independent, and I'd like to cut a new major version when we release the new ece support and runtime-independence, so we have some time to test.
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.
That's what I was saying. Probably not worth the time since it's going to be removed. By the way I have the version that works on your fork (a lot of little conflicts on formatting) so since I already resolved them I can push it on another branch or even udpate this PR once the other one is merged somewhere.
if let Some(signature) = &self.vapid_signature { | ||
headers.push(( | ||
"Authorization", | ||
format!("vapid t={}, k={}", signature.auth_t, signature.auth_k), |
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.
aren't these in the vapid generation already?
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.
TL; DR: AESGCM requires 3 headers: Authorization
, Crypto-Key
and Encryption
, while AES128GCM only needs Authorization
, but its content is different (and a lot easier to understand!) so they must be generated separately
Original message : I'm not sure what you mean... Vapid needs this header (public key + JWT) and it's different on AESGCM (which uses dh
/ecdsa
keys in the Crypto-Key
field, and only the token in Authorization
with a WebPush
prefix). The final RFC puts the dh part back in the Authorization field, under the name k
with a vapid
prefix, and there is no Crypto-Key
field.
Anyway what I mean is the Authorization field is implementation-specific so it's necessarily generated separately, per-implementation, if that's what you meant. This is the part of the code where it was added to the crypto-headers in your previous version (but only the AESGCM version) The VAPID Signature struct only has a function to construct the JWT, which is just one part of the new Authorization header
So maybe we can remove this function from The VapidSignature
impl since it's AESGCM specific and not use into
here.
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.
Yep. I'm not that much into ece encryption anymore, so I trust your research on it... :)
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.
I'll do this little clean-up, with the current Into
implementation looking like it's not AESGCM-specific when it actually is ;)
rust-ece
crate delegationrust-ece
crate delegation
@tiesselune is it now ok to merge? I'm probably not releasing a new version until we get the runtime PR merged too. Then we go one major version up. |
I'd like to make one more commit, but since we're going to merge your other PR and those two have a bunch of smallish formatting conflicts, it's maybe better to update it first once your other PR is merged (I did it once to test the infamous stack-overflow |
And moved it in the `generate_headers_aesgcm` function instead
Re-converting to draft, just noticed I had some weird test failures I still have to investigate. |
This is now tested in `http_ece.rs`
Update: the test failures come from the fact that padding has changed between the previously implemented version and the Pondering if we should re-introduce padding on both schemes |
Note :content-length is not deterministic for AES128GCM
src/error.rs
Outdated
@@ -172,7 +172,7 @@ impl Error for WebPushError { | |||
|
|||
impl fmt::Display for WebPushError { | |||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |||
write!(f, "{}", self.description()) | |||
write!(f, "{}", self) |
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.
This is an infinite recursion causing stack overflow. Perhaps the formatting string should be "{:?}" instead?
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.
@pimeys we found the culprit of this elusive stack overflow! Thanks @koalefant!
I know what happened there. It was a quick deprecation fix since description()
is deprecated on the Error
trait, and it suggests to use the Display
implementation instead or to_string
. I did not really think twice when using the Display
trait... inside the Display
trait 🙄 . Thanks for pointing that out! 😉 I have just submitted a commit that handles the deprecation warning properly, by actually moving the description()
method (that is deprecated, as well as cause()
) in the Display implementation as it is suggested in the up-to-date Error
official doc. I'll take this for a spin later this week and check that everything works properly.
About
This pull request addresses #3 by using the
ece
crate, which is maintained by Mozilla, to implement the Aes128Gcm encoding scheme. This way, it can be easily updated and benefit from security reviews and patches on theece
crate and the crate can focus on the VAPID part of things.For consistency purposes, it entirely delegates ECE encoding to the
ece
crate, including the AesGcm scheme, which implies some refactoring.The actual cryptographic work being implemented and tested in the
ece
crate, the cryptographic functions and tests in thehttp_ece
module can be entirely delegated.Changes summary:
encrypt
function in theHttpEce
struct implementation to support both encodings and use theece
crate, using the same interface to avoid any breaking changeheaders
property of theWebPushPayload
struct from aVec<(&'static str,String)>
to aHashMap<String,String>
for ease of integration and consistency with theece
crate implementation (this might be a breaking change, sinceWebPushPayload
is published at the crate level; it needs more investigation)http-ece
from the tags of the crate, since it is now delegated to another one.Remaining work
This pull request is a draft and it needs a few things before it can be considered:
ece
crate, which I'm working on with the maintainer ( for now,cargo.toml
uses my fork)WebPushPayload
struct needs to be addressedece
crate would allow integrating with its headers function without extra allocation or hashing