/* -*- 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 Web Push API specification. * *

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 RFC 8291. * *

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 ServiceWorker * registration */ @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 RFC 8030 */ @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. * *

This key is used for VAPID, the Voluntary Application Server Identification (VAPID) for Web * Push, from RFC 8292. * * @see applicationServerKey * @see Message Encryption for Web Push */ @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 PushEncryptionKeyName * - p256dh * @see RFC 8291 section 3.1 */ @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 PushEncryptionKeyName * - auth * @see RFC 8291, section 3.2 */ @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 CREATOR = new Parcelable.Creator() { @Override @AnyThread public WebPushSubscription createFromParcel(final Parcel parcel) { return new WebPushSubscription(parcel); } @Override @AnyThread public WebPushSubscription[] newArray(final int size) { return new WebPushSubscription[size]; } }; }