Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 003052a7 authored by Fynn Godau's avatar Fynn Godau
Browse files

Replace hardcoded auth token with auth token fetched via account

parent fd68b188
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ android {

dependencies {
    implementation project(':fake-signature')
    implementation project(':play-services-auth')

    implementation "com.squareup.wire:wire-runtime:$wireVersion"
    implementation "com.android.volley:volley:$volleyVersion"
+9 −2
Original line number Diff line number Diff line
@@ -5,12 +5,19 @@

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-permission android:name="android.permission.INTERNET" />

    <permission
        android:name="com.android.vending.CHECK_LICENSE"
        android:protectionLevel="normal" />

    <uses-permission android:name="android.permission.INTERNET" />

    <uses-permission
        android:name="android.permission.GET_ACCOUNTS"
        android:maxSdkVersion="22" />
    <uses-permission
        android:name="android.permission.USE_CREDENTIALS"
        android:maxSdkVersion="22" />

    <application
        android:forceQueryable="true"
        android:icon="@mipmap/ic_app"
+8 −6
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import okio.ByteString;
public abstract class LicenseRequest<T> extends Request<T> {

    private final String xPsRh;
    private final String auth;
    private static final String TAG = "FakeLicenseRequest";

    private static final int BASE64_FLAGS = Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING;
@@ -49,8 +50,9 @@ public abstract class LicenseRequest<T> extends Request<T> {
    private final Response.Listener<T> successListener;


    protected LicenseRequest(String url, Response.Listener<T> successListener, Response.ErrorListener errorListener) {
    protected LicenseRequest(String url, String auth, Response.Listener<T> successListener, Response.ErrorListener errorListener) {
        super(GET, url, errorListener);
        this.auth = auth;

        this.successListener = successListener;

@@ -143,7 +145,7 @@ public abstract class LicenseRequest<T> extends Request<T> {
    public Map<String, String> getHeaders() {
        return Map.of(
            "X-PS-RH", xPsRh,
            "Authorization", "Bearer ya29.[…]]",
            "Authorization", "Bearer " + auth,
            "Connection", "Keep-Alive"
        );
    }
@@ -162,9 +164,9 @@ public abstract class LicenseRequest<T> extends Request<T> {

    public static class V1 extends LicenseRequest<V1Container> {

        public V1(String packageName, int versionCode, long nonce, Response.Listener<V1Container> successListener, Response.ErrorListener errorListener) {
        public V1(String packageName, String auth, int versionCode, long nonce, Response.Listener<V1Container> successListener, Response.ErrorListener errorListener) {
            super("https://play-fe.googleapis.com/fdfe/apps/checkLicense?pkgn=" + packageName + "&vc=" + versionCode + "&nnc=" + nonce,
                successListener, errorListener
                auth, successListener, errorListener
            );
        }

@@ -187,11 +189,11 @@ public abstract class LicenseRequest<T> extends Request<T> {
    }

    public static class V2 extends LicenseRequest<String> {
        public V2(String packageName, int versionCode, Response.Listener<String> successListener,
        public V2(String packageName, String auth, int versionCode, Response.Listener<String> successListener,
                  Response.ErrorListener errorListener) {
            super(
                "https://play-fe.googleapis.com/fdfe/apps/checkLicenseServerFallback?pkgn=" + packageName + "&vc=" + versionCode,
                successListener, errorListener
                auth, successListener, errorListener
            );
        }

+147 −96
Original line number Diff line number Diff line
@@ -5,6 +5,12 @@

package com.android.vending.licensing;

import static android.accounts.AccountManager.KEY_AUTHTOKEN;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageInfo;
@@ -18,16 +24,19 @@ import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;

import org.microg.gms.auth.AuthConstants;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

public class LicensingService extends Service {
    private static final String TAG = "FakeLicenseService";
    private RequestQueue queue;
    private AccountManager accountManager;

    private static final String KEY_V2_RESULT_JWT = "LICENSE_DATA";

    private final ILicensingService.Stub mLicenseService = new ILicensingService.Stub() {



    private static final String AUTH_TOKEN_SCOPE = "oauth2:https://www.googleapis.com/auth/googleplay";

    /* Possible response codes for checkLicense v1, from
     * https://developer.android.com/google/play/licensing/licensing-reference#server-response-codes and
@@ -73,6 +82,9 @@ public class LicensingService extends Service {
     */
    private static final int ERROR_NON_MATCHING_UID = 0x103;

    private final ILicensingService.Stub mLicenseService = new ILicensingService.Stub() {


        @Override
        public void checkLicense(long nonce, String packageName, ILicenseResultListener listener) throws RemoteException {
            Log.v(TAG, "checkLicense(" + nonce + ", " + packageName + ")");
@@ -85,39 +97,50 @@ public class LicensingService extends Service {
                    Log.e(TAG, "an app illegally tried to request v1 licenses for another app (caller: " + getCallingUid() + ")");
                    listener.verifyLicense(ERROR_NON_MATCHING_UID, null, null);
                } else {
                    Request request =
                        new LicenseRequest.V1(packageName, versionCode, nonce, data -> {

                    Account[] accounts = accountManager.getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE);

                    if (accounts.length == 0) {
                        Log.e(TAG, "not checking license, as user is not signed in");
                    } else accountManager.getAuthToken(
                        accounts[0], AUTH_TOKEN_SCOPE, false,
                        future -> {
                            Request request = null;
                            try {
                                request = new LicenseRequest.V1(
                                    packageName,
                                    future.getResult().getString(KEY_AUTHTOKEN),
                                    versionCode, nonce, data -> {
                                    if (data != null) {
                                    Log.v(TAG, "licenseV1 result was " + data.result + "with signed data " + data.signedData);
                                        Log.v(TAG, "licenseV1 result was " + data.result + " with signed data " +
                                            data.signedData);

                                        try {
                                            if (data.result != null) {
                                                listener.verifyLicense(data.result, data.signedData, data.signature);
                                            } else {
                                                listener.verifyLicense(LICENSED, data.signedData, data.signature);
                                            }
                                } else {
                                    Log.v(TAG, "licenseV1 result was that user has no license");
                                    listener.verifyLicense(NOT_LICENSED, null, null);
                                }
                                        } catch (RemoteException e) {
                                Log.e(TAG, "After returning licenseV1 result, remote threw an Exception.");
                                            Log.e(TAG,
                                                "After telling it the licenseV1 result, remote threw an Exception.");
                                            e.printStackTrace();
                                        }
                                    } else {
                                        Log.v(TAG, "licenseV1 result was that user has no license");
                                        sendError(listener, NOT_LICENSED);
                                    }
                                }, error -> {
                                    Log.e(TAG, "licenseV1 request failed with " + error.toString());
                            try {
                                listener.verifyLicense(ERROR_CONTACTING_SERVER, null, null);
                            } catch (RemoteException e) {
                                Log.e(TAG, "After telling it that licenseV1 had an error when contacting server, remote threw an Exception.");
                                e.printStackTrace();
                                Log.e(TAG, "Caused after network error:");
                                error.printStackTrace();
                            }
                                    sendError(listener, ERROR_CONTACTING_SERVER);
                                });
                            } catch (AuthenticatorException | IOException | OperationCanceledException e) {
                                sendError(listener, ERROR_CONTACTING_SERVER);
                            }

                            request.setShouldCache(false);
                            queue.add(request);
                        }, null);
                }
            } catch (PackageManager.NameNotFoundException e) {
                Log.e(TAG, "an app tried to request v1 licenses for package " + packageName + ", which does not exist");
@@ -138,8 +161,18 @@ public class LicensingService extends Service {
                    Log.e(TAG, "an app illegally tried to request v2 licenses for another app (caller: " + getCallingUid() + ")");
                    listener.verifyLicense(ERROR_NON_MATCHING_UID, new Bundle());
                } else {
                    Request request =
                        new LicenseRequest.V2(packageName, versionCode, jwt -> {
                    Account[] accounts = accountManager.getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE);

                    if (accounts.length == 0) {
                        Log.e(TAG, "not checking license, as user is not signed in");
                    } else accountManager.getAuthToken(
                        accounts[0], AUTH_TOKEN_SCOPE, false,
                        future -> {
                            try {
                                Bundle result = future.getResult(10, TimeUnit.SECONDS);
                                String auth = result.getString(KEY_AUTHTOKEN);

                                Request request = new LicenseRequest.V2(packageName, auth, versionCode, jwt -> {
                                    Log.v(TAG, "LicenseV2 returned JWT license value " + jwt);
                                    Bundle bundle = new Bundle();
                                    bundle.putString(KEY_V2_RESULT_JWT, jwt);
@@ -151,18 +184,18 @@ public class LicensingService extends Service {
                                    }
                                }, error -> {
                                    Log.e(TAG, "licenseV2 request failed with " + error.toString());
                            try {
                                listener.verifyLicense(ERROR_CONTACTING_SERVER, new Bundle());
                            } catch (RemoteException e) {
                                Log.e(TAG, "After telling it that licenseV2 had an error when contacting server, remote threw an Exception.");
                                e.printStackTrace();
                                Log.e(TAG, "Caused after network error:");
                                error.printStackTrace();
                            }
                                    sendError(listener, ERROR_CONTACTING_SERVER);
                                });

                                request.setShouldCache(false);
                                queue.add(request);

                            } catch (AuthenticatorException | IOException | OperationCanceledException e) {
                                sendError(listener, ERROR_CONTACTING_SERVER);
                                e.printStackTrace();
                            }
                        }, null
                    );
                }
            } catch (PackageManager.NameNotFoundException e) {
                Log.e(TAG, "an app tried to request v1 licenses for package " + packageName + ", which does not exist");
@@ -172,8 +205,26 @@ public class LicensingService extends Service {
        }
    };

    private static void sendError(ILicenseResultListener listener, int error) {
        try {
            listener.verifyLicense(error, null, null);
        } catch (RemoteException e) {
            Log.e(TAG, "After telling it that licenseV1 had an error (" + error + "), remote threw an Exception.");
        }
    }

    private static void sendError(ILicenseV2ResultListener listener, int error) {
        try {
            listener.verifyLicense(error, new Bundle());
        } catch (RemoteException e) {
            Log.e(TAG, "After telling it that licenseV2 had an error (" + error + "), remote threw an Exception.");
        }
    }

    public IBinder onBind(Intent intent) {
        queue = Volley.newRequestQueue(this);
        accountManager = AccountManager.get(this);

        return mLicenseService;
    }