401622cc22
-Added new Gradle build system, as it is the required build system
225 lines
7.9 KiB
Java
225 lines
7.9 KiB
Java
/*
|
|
* Copyright (C) 2010 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.google.android.vending.licensing;
|
|
|
|
import com.google.android.vending.licensing.util.Base64;
|
|
import com.google.android.vending.licensing.util.Base64DecoderException;
|
|
|
|
import android.text.TextUtils;
|
|
import android.util.Log;
|
|
|
|
import java.security.InvalidKeyException;
|
|
import java.security.NoSuchAlgorithmException;
|
|
import java.security.PublicKey;
|
|
import java.security.Signature;
|
|
import java.security.SignatureException;
|
|
|
|
/**
|
|
* Contains data related to a licensing request and methods to verify
|
|
* and process the response.
|
|
*/
|
|
class LicenseValidator {
|
|
private static final String TAG = "LicenseValidator";
|
|
|
|
// Server response codes.
|
|
private static final int LICENSED = 0x0;
|
|
private static final int NOT_LICENSED = 0x1;
|
|
private static final int LICENSED_OLD_KEY = 0x2;
|
|
private static final int ERROR_NOT_MARKET_MANAGED = 0x3;
|
|
private static final int ERROR_SERVER_FAILURE = 0x4;
|
|
private static final int ERROR_OVER_QUOTA = 0x5;
|
|
|
|
private static final int ERROR_CONTACTING_SERVER = 0x101;
|
|
private static final int ERROR_INVALID_PACKAGE_NAME = 0x102;
|
|
private static final int ERROR_NON_MATCHING_UID = 0x103;
|
|
|
|
private final Policy mPolicy;
|
|
private final LicenseCheckerCallback mCallback;
|
|
private final int mNonce;
|
|
private final String mPackageName;
|
|
private final String mVersionCode;
|
|
private final DeviceLimiter mDeviceLimiter;
|
|
|
|
LicenseValidator(Policy policy, DeviceLimiter deviceLimiter, LicenseCheckerCallback callback,
|
|
int nonce, String packageName, String versionCode) {
|
|
mPolicy = policy;
|
|
mDeviceLimiter = deviceLimiter;
|
|
mCallback = callback;
|
|
mNonce = nonce;
|
|
mPackageName = packageName;
|
|
mVersionCode = versionCode;
|
|
}
|
|
|
|
public LicenseCheckerCallback getCallback() {
|
|
return mCallback;
|
|
}
|
|
|
|
public int getNonce() {
|
|
return mNonce;
|
|
}
|
|
|
|
public String getPackageName() {
|
|
return mPackageName;
|
|
}
|
|
|
|
private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
|
|
|
|
/**
|
|
* Verifies the response from server and calls appropriate callback method.
|
|
*
|
|
* @param publicKey public key associated with the developer account
|
|
* @param responseCode server response code
|
|
* @param signedData signed data from server
|
|
* @param signature server signature
|
|
*/
|
|
public void verify(PublicKey publicKey, int responseCode, String signedData, String signature) {
|
|
String userId = null;
|
|
// Skip signature check for unsuccessful requests
|
|
ResponseData data = null;
|
|
if (responseCode == LICENSED || responseCode == NOT_LICENSED ||
|
|
responseCode == LICENSED_OLD_KEY) {
|
|
// Verify signature.
|
|
try {
|
|
Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
|
|
sig.initVerify(publicKey);
|
|
sig.update(signedData.getBytes());
|
|
|
|
if (!sig.verify(Base64.decode(signature))) {
|
|
Log.e(TAG, "Signature verification failed.");
|
|
handleInvalidResponse();
|
|
return;
|
|
}
|
|
} catch (NoSuchAlgorithmException e) {
|
|
// This can't happen on an Android compatible device.
|
|
throw new RuntimeException(e);
|
|
} catch (InvalidKeyException e) {
|
|
handleApplicationError(LicenseCheckerCallback.ERROR_INVALID_PUBLIC_KEY);
|
|
return;
|
|
} catch (SignatureException e) {
|
|
throw new RuntimeException(e);
|
|
} catch (Base64DecoderException e) {
|
|
Log.e(TAG, "Could not Base64-decode signature.");
|
|
handleInvalidResponse();
|
|
return;
|
|
}
|
|
|
|
// Parse and validate response.
|
|
try {
|
|
data = ResponseData.parse(signedData);
|
|
} catch (IllegalArgumentException e) {
|
|
Log.e(TAG, "Could not parse response.");
|
|
handleInvalidResponse();
|
|
return;
|
|
}
|
|
|
|
if (data.responseCode != responseCode) {
|
|
Log.e(TAG, "Response codes don't match.");
|
|
handleInvalidResponse();
|
|
return;
|
|
}
|
|
|
|
if (data.nonce != mNonce) {
|
|
Log.e(TAG, "Nonce doesn't match.");
|
|
handleInvalidResponse();
|
|
return;
|
|
}
|
|
|
|
if (!data.packageName.equals(mPackageName)) {
|
|
Log.e(TAG, "Package name doesn't match.");
|
|
handleInvalidResponse();
|
|
return;
|
|
}
|
|
|
|
if (!data.versionCode.equals(mVersionCode)) {
|
|
Log.e(TAG, "Version codes don't match.");
|
|
handleInvalidResponse();
|
|
return;
|
|
}
|
|
|
|
// Application-specific user identifier.
|
|
userId = data.userId;
|
|
if (TextUtils.isEmpty(userId)) {
|
|
Log.e(TAG, "User identifier is empty.");
|
|
handleInvalidResponse();
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch (responseCode) {
|
|
case LICENSED:
|
|
case LICENSED_OLD_KEY:
|
|
int limiterResponse = mDeviceLimiter.isDeviceAllowed(userId);
|
|
handleResponse(limiterResponse, data);
|
|
break;
|
|
case NOT_LICENSED:
|
|
handleResponse(Policy.NOT_LICENSED, data);
|
|
break;
|
|
case ERROR_CONTACTING_SERVER:
|
|
Log.w(TAG, "Error contacting licensing server.");
|
|
handleResponse(Policy.RETRY, data);
|
|
break;
|
|
case ERROR_SERVER_FAILURE:
|
|
Log.w(TAG, "An error has occurred on the licensing server.");
|
|
handleResponse(Policy.RETRY, data);
|
|
break;
|
|
case ERROR_OVER_QUOTA:
|
|
Log.w(TAG, "Licensing server is refusing to talk to this device, over quota.");
|
|
handleResponse(Policy.RETRY, data);
|
|
break;
|
|
case ERROR_INVALID_PACKAGE_NAME:
|
|
handleApplicationError(LicenseCheckerCallback.ERROR_INVALID_PACKAGE_NAME);
|
|
break;
|
|
case ERROR_NON_MATCHING_UID:
|
|
handleApplicationError(LicenseCheckerCallback.ERROR_NON_MATCHING_UID);
|
|
break;
|
|
case ERROR_NOT_MARKET_MANAGED:
|
|
handleApplicationError(LicenseCheckerCallback.ERROR_NOT_MARKET_MANAGED);
|
|
break;
|
|
default:
|
|
Log.e(TAG, "Unknown response code for license check.");
|
|
handleInvalidResponse();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Confers with policy and calls appropriate callback method.
|
|
*
|
|
* @param response
|
|
* @param rawData
|
|
*/
|
|
private void handleResponse(int response, ResponseData rawData) {
|
|
// Update policy data and increment retry counter (if needed)
|
|
mPolicy.processServerResponse(response, rawData);
|
|
|
|
// Given everything we know, including cached data, ask the policy if we should grant
|
|
// access.
|
|
if (mPolicy.allowAccess()) {
|
|
mCallback.allow(response);
|
|
} else {
|
|
mCallback.dontAllow(response);
|
|
}
|
|
}
|
|
|
|
private void handleApplicationError(int code) {
|
|
mCallback.applicationError(code);
|
|
}
|
|
|
|
private void handleInvalidResponse() {
|
|
mCallback.dontAllow(Policy.NOT_LICENSED);
|
|
}
|
|
}
|