pimeys / rust-web-push

A Web Push library for Rust
Apache License 2.0
113 stars 21 forks source link

Test on Apple IOS devices #40

Closed andyblarblar closed 1 year ago

andyblarblar commented 1 year ago

If you haven't seen, Apple has finally added support for web push on IOS in IOS 16.4. This is very exciting, since web-push will finally be truely cross platform!

Currently, this crate is untested with apple push. It should work in theory though.

Dygear commented 1 year ago

The problem that I am having is that it seems that Apple encoding type is always of the aesgcm type that is no longer supported by this crate. Or is there something that I am doing wrong or there is a way for me to force it into an aes128gcm? It's actually the same with Mozilla, that is also returning the aesgcm type.

andyblarblar commented 1 year ago

What do you mean by 'returning aesgcm type'? I've been attempting to look into what versions each browser supports and can't find a good reference. I think it may be possible to add support for aesgcm back to this crate using the ece crate (I removed the old version cause it was implemented in this crate, very insecure), but tbh my cryptography knowledge is pretty poor so it may take a while.

Dygear commented 1 year ago

Ah, I found out what my problem was!

async function pushSubscribe(subscription) {
    const key = subscription.getKey('p256dh');
    const token = subscription.getKey('auth');
    console.dir(PushManager.supportedContentEncodings);
    const contentEncoding = (PushManager.supportedContentEncodings || ['aesgcm'])[0];

    let options = {
        endpoint: subscription.endpoint,
        publicKey: key ? btoa(String.fromCharCode.apply(null, new Uint8Array(key))) : null,
        authToken: token ? btoa(String.fromCharCode.apply(null, new Uint8Array(token))) : null,
        encoding: contentEncoding,
        userId: <?=$_SESSION['userId']?>,
    };

    console.dir(options);
    let sub = await fetch('/push/subscribe.php', {
        credentials: 'include',
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        body: "json=" + JSON.stringify(options),
    });
    console.dir(sub);
    console.log(sub.text());
}

This line ... const contentEncoding = (PushManager.supportedContentEncodings || ['aesgcm'])[0]; ... was only accepting the aesgcm type. But if I added PushManager.supportedContentEncodings and did a console.dir on that, I saw this array. `0: "aesgcm", 1: "aes128gcm"`` So by changing that line above to this it should work now ...

const contentEncoding = (PushManager.supportedContentEncodings || ['aes128gcm'])[0];

Now it should work in Chrome, FireFox and Safari.

...

[EDIT]

Now that I'm trying to make a subscription, and passing aes128gcm into the encoding options object. I'm still getting the same publicKey and authToken. I wonder if these are compatible with each other or it's just returning the already established key but with the old credentials.

Dygear commented 1 year ago

Using the example folder's examples/simple_send.rs and a VAPID file I created with the following command line options

openssl ecparam -genkey -name prime256v1 -out VAPID.pem
openssl ec -in VAPID.pem -pubout -outform DER|tail -c 65|base64|tr '/+' '_-'|tr -d '\n'

I have the following file ...

VAPID.pem

-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIDJWnj9AWwDgl32486OBZdlVPmaKmSQFDln0C6NuJX01oAoGCCqGSM49
AwEHoUQDQgAEQXkY0u4Shv+oTcpPpbxZ9Xs/yUBr6WLKfLMv/TzpCzymkTAT6zRZ
RxzUNRMgqo7OJVFdR35Wo2kO/xpRZ8z65Q==
-----END EC PRIVATE KEY-----

Looks like the current version of the create doesn't like this pem file format. I modified the end of the examples/simple_send.rs

from this:

    # normal fn main function above this point.

    let response = client.send(builder.build()?).await?;
    println!("Sent: {:?}", response);

    Ok(())
}

to this

    # normal fn main function above this point.

    let build = match builder.build() {
        Ok(build) => build,
        Err(e) => panic!("Builder Error: {e}"),
    };

    match client.send(build).await {
        Ok(_) => println!("Send Success"),
        Err(e) => println!("Send Error: {e}"),
    };

    Ok(())
}

Producing the following ...

thread 'main' panicked at 'Builder Error: The request is having invalid cryptographic keys', examples/simple_send.rs:101:19 note: run with RUST_BACKTRACE=1 environment variable to display a backtrace.

That's from the WebPushError struct, WebPushError::InvalidCryptoKeys match arm. That's gotta be coming from the src/vapid/builder.rs.

        if found_sec1 {
            let key = sec1_decode::parse_pem(buffer.as_bytes()).map_err(|_| WebPushError::InvalidCryptoKeys)?;
            Ok(ES256KeyPair::from_bytes(&key.key).map_err(|_| WebPushError::InvalidCryptoKeys)?)
        }

So it' has to be this arm that I'm failing. And It looks like it's from the sec1_decode create that actually handles that part of this. Hey @andyblarblar ... :)

andyblarblar commented 1 year ago

Please make a new issue for anything not related to apple push support.

andyblarblar commented 1 year ago

This has been tested to work on IoS 16.4.