diff options
Diffstat (limited to 'mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebPushSubscription.java')
-rw-r--r-- | mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebPushSubscription.java | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebPushSubscription.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebPushSubscription.java new file mode 100644 index 0000000000..7ce9a3d60c --- /dev/null +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebPushSubscription.java @@ -0,0 +1,180 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * vim: ts=4 sw=4 expandtab: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.geckoview; + +import android.os.Parcel; +import android.os.Parcelable; +import androidx.annotation.AnyThread; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import java.util.Arrays; +import org.mozilla.gecko.util.GeckoBundle; + +/** + * This class represents a single Web Push subscription, as described in the <a + * href="https://www.w3.org/TR/push-api/">Web Push API</a> specification. + * + * <p>This is a low-level interface, allowing applications to do all of the heavy lifting + * themselves. It is recommended that consumers have a thorough understanding of the Web Push API, + * especially <a href="https://tools.ietf.org/html/rfc8291">RFC 8291</a>. + * + * <p>Only trivial sanity checks are performed on the values held here. The application must ensure + * it is generating compliant keys/secrets itself. + */ +public class WebPushSubscription implements Parcelable { + private static final int P256_PUBLIC_KEY_LENGTH = 65; + + /** + * The Service Worker scope associated with this subscription. + * + * @see <a + * href="https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register">ServiceWorker + * registration</a> + */ + @NonNull public final String scope; + + /** + * The Web Push endpoint for this subscription. This is the URL of a web service which implements + * the Web Push protocol. + * + * @see <a href="https://tools.ietf.org/html/rfc8030#section-5">RFC 8030</a> + */ + @NonNull public final String endpoint; + + /** + * This is an optional public key provided by the application server to authenticate itself with + * the endpoint, formatted according to X9.62. + * + * <p>This key is used for VAPID, the Voluntary Application Server Identification (VAPID) for Web + * Push, from <a href="https://tools.ietf.org/html/rfc8292">RFC 8292</a>. + * + * @see <a + * href="https://www.w3.org/TR/push-api/#dom-pushsubscriptionoptions-applicationserverkey">applicationServerKey</a> + * @see <a href="https://tools.ietf.org/html/rfc8291">Message Encryption for Web Push</a> + */ + @Nullable public final byte[] appServerKey; + + /** + * The P-256 EC public key, formatted as X9.62, generated by the embedder, to be provided to the + * app server for message encryption. + * + * @see <a + * href="https://www.w3.org/TR/push-api/#dom-pushencryptionkeyname-p256dh">PushEncryptionKeyName + * - p256dh</a> + * @see <a href="https://tools.ietf.org/html/rfc8291#section-3.1">RFC 8291 section 3.1</a> + */ + @NonNull public final byte[] browserPublicKey; + + /** + * 16 byte secret key, generated by the embedder, to be provided to the app server for use in + * encrypting and authenticating messages sent to the {@link #endpoint}. + * + * @see <a + * href="https://www.w3.org/TR/push-api/#dom-pushencryptionkeyname-auth">PushEncryptionKeyName + * - auth</a> + * @see <a href="https://tools.ietf.org/html/rfc8291#section-3.2">RFC 8291, section 3.2</a> + */ + @NonNull public final byte[] authSecret; + + @SuppressWarnings("checkstyle:javadocmethod") + public WebPushSubscription( + final @NonNull String scope, + final @NonNull String endpoint, + final @Nullable byte[] appServerKey, + final @NonNull byte[] browserPublicKey, + final @NonNull byte[] authSecret) { + this.scope = scope; + this.endpoint = endpoint; + this.appServerKey = appServerKey; + this.browserPublicKey = browserPublicKey; + this.authSecret = authSecret; + + if (appServerKey != null) { + if (appServerKey.length != P256_PUBLIC_KEY_LENGTH) { + throw new IllegalArgumentException( + String.format("appServerKey should be %d bytes", P256_PUBLIC_KEY_LENGTH)); + } + + if (Arrays.equals(appServerKey, browserPublicKey)) { + throw new IllegalArgumentException("appServerKey and browserPublicKey must differ"); + } + } + + if (browserPublicKey.length != P256_PUBLIC_KEY_LENGTH) { + throw new IllegalArgumentException( + String.format("browserPublicKey should be %d bytes", P256_PUBLIC_KEY_LENGTH)); + } + + if (authSecret.length != 16) { + throw new IllegalArgumentException("authSecret must be 128 bits"); + } + } + + private WebPushSubscription(final Parcel in) { + this.scope = in.readString(); + this.endpoint = in.readString(); + + if (ParcelableUtils.readBoolean(in)) { + this.appServerKey = new byte[P256_PUBLIC_KEY_LENGTH]; + in.readByteArray(this.appServerKey); + } else { + appServerKey = null; + } + + this.browserPublicKey = new byte[P256_PUBLIC_KEY_LENGTH]; + in.readByteArray(this.browserPublicKey); + + this.authSecret = new byte[16]; + in.readByteArray(this.authSecret); + } + + /* package */ GeckoBundle toBundle() { + final GeckoBundle bundle = new GeckoBundle(5); + bundle.putString("scope", scope); + bundle.putString("endpoint", endpoint); + if (appServerKey != null) { + bundle.putString("appServerKey", Base64Utils.encode(appServerKey)); + } + bundle.putString("browserPublicKey", Base64Utils.encode(browserPublicKey)); + bundle.putString("authSecret", Base64Utils.encode(authSecret)); + return bundle; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(final Parcel out, final int flags) { + out.writeString(scope); + out.writeString(endpoint); + + ParcelableUtils.writeBoolean(out, appServerKey != null); + if (appServerKey != null) { + out.writeByteArray(appServerKey); + } + + out.writeByteArray(browserPublicKey); + out.writeByteArray(authSecret); + } + + public static final Parcelable.Creator<WebPushSubscription> CREATOR = + new Parcelable.Creator<WebPushSubscription>() { + @Override + @AnyThread + public WebPushSubscription createFromParcel(final Parcel parcel) { + return new WebPushSubscription(parcel); + } + + @Override + @AnyThread + public WebPushSubscription[] newArray(final int size) { + return new WebPushSubscription[size]; + } + }; +} |