Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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
* 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
* @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
* - 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
* - 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];
}
};
}