diff options
author | samalin <samalin@google.com> | 2021-01-22 19:57:06 +0800 |
---|---|---|
committer | Meng Wang <mewan@google.com> | 2021-02-02 21:24:06 +0000 |
commit | 5bf177361dae4fa3e626f450f46cd703ef678c7c (patch) | |
tree | 1afff168991bf40be9086505a4a020086191067c /java/com | |
parent | 9a499beea421646f04274919e0d9b87fe40af839 (diff) | |
download | service_entitlement-5bf177361dae4fa3e626f450f46cd703ef678c7c.tar.gz |
EAP AKA Challenge response not accepted by server
Correct the parameter of calling the method of generate IMSI EAP
and also format the code style
Bug: 177544547
Test: atest EapAkaResponseTest and Verified CSpire VoWiFi entitlement flow with this CL.
Summary
-------
arm64-v8a service-entitlement-tests: Passed: 28, Failed: 0, Ignored: 0, Assumption Failed: 0,
Change-Id: I28766caf800139f6c4244be5a65b4f508c539fe0
Merged-In: I28766caf800139f6c4244be5a65b4f508c539fe0
(cherry picked from commit fb4a25e1de19e98950d3adb6041818e70a5eb8fe)
Diffstat (limited to 'java/com')
14 files changed, 387 insertions, 257 deletions
diff --git a/java/com/android/libraries/entitlement/EsimOdsaOperation.java b/java/com/android/libraries/entitlement/EsimOdsaOperation.java index d7d23ac..ce764f2 100644 --- a/java/com/android/libraries/entitlement/EsimOdsaOperation.java +++ b/java/com/android/libraries/entitlement/EsimOdsaOperation.java @@ -24,22 +24,38 @@ import com.google.auto.value.AutoValue; */ @AutoValue public abstract class EsimOdsaOperation { - /** OSDA operation: CheckEligibility. */ + /** + * OSDA operation: CheckEligibility. + */ public static final String OPERATION_CHECK_ELIGIBILITY = "CheckEligibility"; - /** OSDA operation: ManageSubscription. */ + /** + * OSDA operation: ManageSubscription. + */ public static final String OPERATION_MANAGE_SUBSCRIPTION = "ManageSubscription"; - /** OSDA operation: ManageService. */ + /** + * OSDA operation: ManageService. + */ public static final String OPERATION_MANAGE_SERVICE = "ManageService"; - /** OSDA operation: AcquireConfiguration. */ + /** + * OSDA operation: AcquireConfiguration. + */ public static final String OPERATION_ACQUIRE_CONFIGURATION = "AcquireConfiguration"; - /** Indicates that operation_type is not set. */ + /** + * Indicates that operation_type is not set. + */ static final int OPERATION_TYPE_NOT_SET = -1; - /** To activate a subscription, used by {@link #OPERATION_MANAGE_SUBSCRIPTION}. */ + /** + * To activate a subscription, used by {@link #OPERATION_MANAGE_SUBSCRIPTION}. + */ public static final int OPERATION_TYPE_SUBSCRIBE = 0; - /** To cancel a subscription, used by {@link #OPERATION_MANAGE_SUBSCRIPTION}. */ + /** + * To cancel a subscription, used by {@link #OPERATION_MANAGE_SUBSCRIPTION}. + */ public static final int OPERATION_TYPE_UNSUBSCRIBE = 1; - /** To manage an existing subscription, for {@link #OPERATION_MANAGE_SUBSCRIPTION}. */ + /** + * To manage an existing subscription, for {@link #OPERATION_MANAGE_SUBSCRIPTION}. + */ public static final int OPERATION_TYPE_CHANGE_SUBSCRIPTION = 2; /** * To transfer a subscription from an existing device, used by {@link @@ -51,21 +67,32 @@ public abstract class EsimOdsaOperation { * {@link #OPERATION_MANAGE_SUBSCRIPTION}. */ public static final int OPERATION_TYPE_UPDATE_SUBSCRIPTION = 4; - /** To activate a service, used by {@link #OPERATION_MANAGE_SERVICE}. */ + /** + * To activate a service, used by {@link #OPERATION_MANAGE_SERVICE}. + */ public static final int OPERATION_TYPE_ACTIVATE_SERVICE = 10; - /** To deactivate a service, used by {@link #OPERATION_MANAGE_SERVICE}. */ + /** + * To deactivate a service, used by {@link #OPERATION_MANAGE_SERVICE}. + */ public static final int OPERATION_TYPE_DEACTIVATE_SERVICE = 11; - /** Indicates the companion device carries the same MSISDN as the primary device. */ + /** + * Indicates the companion device carries the same MSISDN as the primary device. + */ public static final String COMPANION_SERVICE_SHAERED_NUMBER = "SharedNumber"; - /** Indicates the companion device carries a different MSISDN as the primary device. */ + /** + * Indicates the companion device carries a different MSISDN as the primary device. + */ public static final String COMPANION_SERVICE_DIFFERENT_NUMBER = "DiffNumber"; - /** Returns the eSIM ODSA operation. Used by HTTP parameter "operation". */ + /** + * Returns the eSIM ODSA operation. Used by HTTP parameter "operation". + */ public abstract String operation(); /** - * Returns the detiled type of the eSIM ODSA operation. Used by HTTP parameter "operation_type". + * Returns the detiled type of the eSIM ODSA operation. Used by HTTP parameter + * "operation_type". */ public abstract int operationType(); @@ -81,7 +108,8 @@ public abstract class EsimOdsaOperation { public abstract String companionTerminalVendor(); /** - * Returns the model of the companion device. Used by HTTP parameter "companion_terminal_model". + * Returns the model of the companion device. Used by HTTP parameter + * "companion_terminal_model". */ public abstract String companionTerminalModel(); @@ -104,16 +132,20 @@ public abstract class EsimOdsaOperation { public abstract String companionTerminalService(); /** - * Returns the ICCID of the companion device. Used by HTTP parameter "companion_terminal_iccid". + * Returns the ICCID of the companion device. Used by HTTP parameter + * "companion_terminal_iccid". */ public abstract String companionTerminalIccid(); /** - * Returns the ICCID of the companion device. Used by HTTP parameter "companion_terminal_iccid". + * Returns the ICCID of the companion device. Used by HTTP parameter + * "companion_terminal_iccid". */ public abstract String companionTerminalEid(); - /** Returns the ICCID of the primary device eSIM. Used by HTTP parameter "terminal_eid". */ + /** + * Returns the ICCID of the primary device eSIM. Used by HTTP parameter "terminal_eid". + */ public abstract String terminalIccid(); /** @@ -128,7 +160,9 @@ public abstract class EsimOdsaOperation { */ public abstract String targetTerminalId(); - /** Returns the ICCID primary device eSIM. Used by HTTP parameter "target_terminal_iccid". */ + /** + * Returns the ICCID primary device eSIM. Used by HTTP parameter "target_terminal_iccid". + */ public abstract String targetTerminalIccid(); /** @@ -137,7 +171,9 @@ public abstract class EsimOdsaOperation { */ public abstract String targetTerminalEid(); - /** Returns a new {@link Builder} object. */ + /** + * Returns a new {@link Builder} object. + */ public static Builder builder() { return new AutoValue_EsimOdsaOperation.Builder().setOperationType(OPERATION_TYPE_NOT_SET); } @@ -146,10 +182,8 @@ public abstract class EsimOdsaOperation { * Builder. * * <p>For ODSA, the rule of which parameters are required varies or each - * operation/opeation_type. - * The Javadoc below gives high-level description, but please refer to GMSA spec TS.43 section - * 6.2 - * for details. + * operation/opeation_type. The Javadoc below gives high-level description, but please refer to + * GMSA spec TS.43 section 6.2 for details. */ @AutoValue.Builder public abstract static class Builder { diff --git a/java/com/android/libraries/entitlement/ServiceEntitlement.java b/java/com/android/libraries/entitlement/ServiceEntitlement.java index abf6ace..be50605 100644 --- a/java/com/android/libraries/entitlement/ServiceEntitlement.java +++ b/java/com/android/libraries/entitlement/ServiceEntitlement.java @@ -18,28 +18,38 @@ package com.android.libraries.entitlement; import android.content.Context; +import androidx.annotation.Nullable; + import com.android.libraries.entitlement.eapaka.EapAkaApi; import com.google.common.annotations.VisibleForTesting; import java.util.List; -import androidx.annotation.Nullable; - /** * Implemnets protocol for carrier service entitlement configuration query and operation, based on * GSMA TS.43 spec. */ public class ServiceEntitlement { - /** App ID for Voice-Over-LTE entitlement. */ + /** + * App ID for Voice-Over-LTE entitlement. + */ public static final String APP_VOLTE = "ap2003"; - /** App ID for Voice-Over-WiFi entitlement. */ + /** + * App ID for Voice-Over-WiFi entitlement. + */ public static final String APP_VOWIFI = "ap2004"; - /** App ID for SMS-Over-IP entitlement. */ + /** + * App ID for SMS-Over-IP entitlement. + */ public static final String APP_SMSOIP = "ap2005"; - /** App ID for on device service activation (OSDA) for companion device. */ + /** + * App ID for on device service activation (OSDA) for companion device. + */ public static final String APP_ODSA_COMPANION = "ap2006"; - /** App ID for on device service activation (OSDA) for primary device. */ + /** + * App ID for on device service activation (OSDA) for primary device. + */ public static final String APP_ODSA_PRIMARY = "ap2009"; private final Context context; @@ -54,9 +64,9 @@ public class ServiceEntitlement { * @param carrierConfig carrier specific configs used in the queries and operations. * @param simSubscriptionId the subscroption ID of the carrier's SIM on device. This indicates * which SIM to retrieve IMEI/IMSI from and perform EAP-AKA - * authentication with. See {@link - * android.telephony.SubscriptionManager} for how to get the - * subscroption ID. + * authentication with. See + * {@link android.telephony.SubscriptionManager} + * for how to get the subscroption ID. */ public ServiceEntitlement(Context context, CarrierConfig carrierConfig, int simSubscriptionId) { this.context = context; @@ -78,10 +88,8 @@ public class ServiceEntitlement { * <p>Supported {@code appId}: {@link #APP_VOLTE}, {@link #APP_VOWIFI}, {@link #APP_SMSOIP}. * * <p>This method sends an HTTP GET request to entitlement server, responds to EAP-AKA - * challenge - * if needed, and returns the raw configuration doc as a string. The following parameters are - * set - * in the HTTP request: + * challenge if needed, and returns the raw configuration doc as a string. The following + * parameters are set in the HTTP request: * * <ul> * <li>"app": {@code appId} @@ -119,9 +127,8 @@ public class ServiceEntitlement { /** * Retrieves service entitlement configurations for multiple app IDs in one HTTP - * request/response. - * For on device service activation (ODSA) of eSIM for companion/primary devices, use {@link - * #performEsimOdsa} instead. + * request/response. For on device service activation (ODSA) of eSIM for companion/primary + * devices, use {@link #performEsimOdsa} instead. * * <p>Same with {@link #queryEntitlementStatus(String, ServiceEntitlementRequest)} except that * multiple "app" parameters will be set in the HTTP request, in the order as they appear in @@ -138,10 +145,10 @@ public class ServiceEntitlement { * * <p>Supported {@code appId}: {@link #APP_ODSA_COMPANION}, {@link #APP_ODSA_PRIMARY}. * - * <p>Similar to {@link #queryEntitlementStatus(String, ServiceEntitlementRequest)}, this method - * sends an HTTP GET request to entitlement server, responds to EAP-AKA challenge if needed, and - * returns the raw configuration doc as a string. Additional parameters from {@code operation} - * are set to the HTTP request. See {@link EsimOdsaOperation} for details. + * <p>Similar to {@link #queryEntitlementStatus(String, ServiceEntitlementRequest)}, this + * method sends an HTTP GET request to entitlement server, responds to EAP-AKA challenge if + * needed, and returns the raw configuration doc as a string. Additional parameters from {@code + * operation} are set to the HTTP request. See {@link EsimOdsaOperation} for details. */ public String performEsimOdsa( String appId, ServiceEntitlementRequest request, EsimOdsaOperation operation) diff --git a/java/com/android/libraries/entitlement/ServiceEntitlementException.java b/java/com/android/libraries/entitlement/ServiceEntitlementException.java index 499a032..fa476ce 100644 --- a/java/com/android/libraries/entitlement/ServiceEntitlementException.java +++ b/java/com/android/libraries/entitlement/ServiceEntitlementException.java @@ -16,18 +16,22 @@ package com.android.libraries.entitlement; -/** Indicates errors happened in retrieving service entitlement configuration. */ +/** + * Indicates errors happened in retrieving service entitlement configuration. + */ public class ServiceEntitlementException extends Exception { - /** Unknown error. */ + /** + * Unknown error. + */ public static final int ERROR_UNKNOWN = 0; - /** Android telephony is unable to provide info like IMSI, e.g. when modem crashed. */ + /** + * Android telephony is unable to provide info like IMSI, e.g. when modem crashed. + */ public static final int ERROR_PHONE_NOT_AVAILABLE = 1; /** * SIM not returning a response to the EAP-AKA challenge, e.g. when the challenge is invalid. - * This - * can happen only when an embedded EAP-AKA challange is conducted, as per GMSA spec TS.43 - * section - * 2.6.1. + * This can happen only when an embedded EAP-AKA challange is conducted, as per GMSA spec TS.43 + * section 2.6.1. */ public static final int ERROR_ICC_AUTHENTICATION_NOT_AVAILABLE = 2; /** @@ -51,13 +55,17 @@ public class ServiceEntitlementException extends Exception { // TODO(b/177544547): add implementation } - /** Returns the error code, see {@link #ERROR_*}. */ + /** + * Returns the error code, see {@link #ERROR_*}. + */ public int getErrorCode() { // TODO(b/177544547): add implementation return ERROR_UNKNOWN; } - /** Returns the HTTP status code returned by entitlement server; 0 if unavailable. */ + /** + * Returns the HTTP status code returned by entitlement server; 0 if unavailable. + */ public int getHttpStatus() { // TODO(b/177544547): add implementation return ERROR_SEVER_NOT_CONNECTABLE; diff --git a/java/com/android/libraries/entitlement/ServiceEntitlementRequest.java b/java/com/android/libraries/entitlement/ServiceEntitlementRequest.java index 6bbfee6..9db5246 100644 --- a/java/com/android/libraries/entitlement/ServiceEntitlementRequest.java +++ b/java/com/android/libraries/entitlement/ServiceEntitlementRequest.java @@ -21,12 +21,18 @@ import android.os.Build.VERSION; import com.google.auto.value.AutoValue; -/** Service entitlement HTTP request parameters, as defiend in GSMA spec TS.43 section 2.2. */ +/** + * Service entitlement HTTP request parameters, as defiend in GSMA spec TS.43 section 2.2. + */ @AutoValue public abstract class ServiceEntitlementRequest { - /** Disables notification token. */ + /** + * Disables notification token. + */ public static final int NOTICATION_ACTION_DISABLE = 0; - /** Enables FCM notification token. */ + /** + * Enables FCM notification token. + */ public static final int NOTICATION_ACTION_ENABLE_FCM = 2; /** @@ -41,7 +47,9 @@ public abstract class ServiceEntitlementRequest { */ public abstract String entitlementVersion(); - /** Returns the authentication token. Used by HTTP parameter "token". */ + /** + * Returns the authentication token. Used by HTTP parameter "token". + */ public abstract String authenticationToken(); /** @@ -49,13 +57,19 @@ public abstract class ServiceEntitlementRequest { */ public abstract String terminalId(); - /** Returns the OEM of the device. Used by HTTP parameter "terminal_vendor". */ + /** + * Returns the OEM of the device. Used by HTTP parameter "terminal_vendor". + */ public abstract String terminalVendor(); - /** Returns the model of the device. Used by HTTP parameter "terminal_model". */ + /** + * Returns the model of the device. Used by HTTP parameter "terminal_model". + */ public abstract String terminalModel(); - /** Returns the software version of the device. Used by HTTP parameter "terminal_sw_version". */ + /** + * Returns the software version of the device. Used by HTTP parameter "terminal_sw_version". + */ public abstract String terminalSoftwareVersion(); /** @@ -72,8 +86,7 @@ public abstract class ServiceEntitlementRequest { /** * Returns the FCM registration token used to register for entitlement configuration request - * from - * network. Used by HTTP parameter "notif_token". + * from network. Used by HTTP parameter "notif_token". */ public abstract String notificationToken(); @@ -86,7 +99,9 @@ public abstract class ServiceEntitlementRequest { */ public abstract int notificationAction(); - /** Returns a new {@link Builder} object. */ + /** + * Returns a new {@link Builder} object. + */ public static Builder builder() { return new AutoValue_ServiceEntitlementRequest.Builder() .setConfigurationVersion(0) @@ -102,7 +117,9 @@ public abstract class ServiceEntitlementRequest { .setNotificationAction(NOTICATION_ACTION_ENABLE_FCM); } - /** Builder. */ + /** + * Builder. + */ @AutoValue.Builder public abstract static class Builder { /** @@ -130,7 +147,8 @@ public abstract class ServiceEntitlementRequest { public abstract Builder setAuthenticationToken(String value); /** - * Sets the unique identifier of the device like IMEI. Used by HTTP parameter "terminal_id". + * Sets the unique identifier of the device like IMEI. Used by HTTP parameter + * "terminal_id". * * <p>If not set, will use the device IMEI. */ @@ -175,8 +193,7 @@ public abstract class ServiceEntitlementRequest { /** * Sets the FCM registration token used to register for entitlement configuration request - * from - * network. Used by HTTP parameter "notif_token". + * from network. Used by HTTP parameter "notif_token". * * <p>Optional. */ diff --git a/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java b/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java index b9eaadd..a145ebc 100644 --- a/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java +++ b/java/com/android/libraries/entitlement/eapaka/EapAkaApi.java @@ -22,6 +22,8 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; +import androidx.annotation.Nullable; + import com.android.libraries.entitlement.ServiceEntitlementException; import com.android.libraries.entitlement.ServiceEntitlementRequest; import com.android.libraries.entitlement.http.HttpClient; @@ -30,14 +32,12 @@ import com.android.libraries.entitlement.http.HttpConstants.RequestMethod; import com.android.libraries.entitlement.http.HttpRequest; import com.android.libraries.entitlement.http.HttpResponse; -import org.json.JSONException; -import org.json.JSONObject; - -import androidx.annotation.Nullable; - import com.google.common.annotations.VisibleForTesting; import com.google.common.net.HttpHeaders; +import org.json.JSONException; +import org.json.JSONObject; + import java.net.CookieHandler; import java.net.CookieManager; @@ -46,52 +46,72 @@ public class EapAkaApi { public static final String EAP_CHALLENGE_RESPONSE = "eap-relay-packet"; - /** Current version of the entitlement configuration. */ + /** + * Current version of the entitlement configuration. + */ private static final String VERS = "vers"; - /** Version of the entitlement configuration. */ + /** + * Version of the entitlement configuration. + */ private static final String ENTITLEMENT_VERSION = "entitlement_version"; /** - * Unique identifier for the device. Refer to {@link - * android.telephony.TelephonyManager#getImei()}. + * Unique identifier for the device. Refer to + * {@link android.telephony.TelephonyManager#getImei()}. */ private static final String TERMINAL_ID = "terminal_id"; - /** Device manufacturer. */ + /** + * Device manufacturer. + */ private static final String TERMINAL_VENDOR = "terminal_vendor"; - /** Device model. */ + /** + * Device model. + */ private static final String TERMINAL_MODEL = "terminal_model"; - /** Device software version. */ + /** + * Device software version. + */ private static final String TERMIAL_SW_VERSION = "terminal_sw_version"; - /** Identifier for the requested entitlement. */ + /** + * Identifier for the requested entitlement. + */ private static final String APP = "app"; - /** NAI needed for EAP-AKA authentication. */ + /** + * NAI needed for EAP-AKA authentication. + */ private static final String EAP_ID = "EAP_ID"; private static final String IMSI = "IMSI"; private static final String TOKEN = "token"; - /** Action for the notification registration token. */ + /** + * Action for the notification registration token. + */ private static final String NOTIF_ACTION = "notif_action"; - /** Attribute name of the notification registration token. */ + /** + * Attribute name of the notification registration token. + */ private static final String NOTIF_TOKEN = "notif_token"; - /** Attribute name of the app version. */ + /** + * Attribute name of the app version. + */ private static final String APP_VERSION = "app_version"; - /** Attribute name of the app name. */ + /** + * Attribute name of the app name. + */ private static final String APP_NAME = "app_name"; - private final Context context; - private final int simSubscriptionId; - private final HttpClient httpClient; + private final Context mContext; + private final int mSimSubscriptionId; + private final HttpClient mHttpClient; public EapAkaApi(Context context, int simSubscriptionId) { - this.context = context; - this.simSubscriptionId = simSubscriptionId; - this.httpClient = new HttpClient(); + this(context, simSubscriptionId, new HttpClient()); } @VisibleForTesting EapAkaApi(Context context, int simSubscriptionId, HttpClient httpClient) { - this.context = context; - this.simSubscriptionId = simSubscriptionId; - this.httpClient = httpClient; + this.mContext = context; + this.mSimSubscriptionId = simSubscriptionId; + this.mHttpClient = httpClient; } /** @@ -117,7 +137,7 @@ public class EapAkaApi { "application/vnd.gsma.eap-relay.v1.0+json, text/vnd.wap" + ".connectivity-xml") .build(); - HttpResponse response = httpClient.request(httpRequest); + HttpResponse response = mHttpClient.request(httpRequest); if (response == null) { throw new ServiceEntitlementException("Null http response"); } @@ -128,7 +148,7 @@ public class EapAkaApi { String akaChallengeResponse = new EapAkaResponse( new JSONObject(response.body()).getString(EAP_CHALLENGE_RESPONSE)) - .getEapAkaChallengeResponse(context, simSubscriptionId); + .getEapAkaChallengeResponse(mContext, mSimSubscriptionId); JSONObject postData = new JSONObject(); postData.put(EAP_CHALLENGE_RESPONSE, akaChallengeResponse); return challengeResponse(postData, serverUrl); @@ -160,7 +180,7 @@ public class EapAkaApi { "application/vnd.gsma.eap-relay.v1.0+json") .build(); - HttpResponse response = httpClient.request(request); + HttpResponse response = mHttpClient.request(request); if (response == null || response.contentType() != ContentType.XML) { throw new ServiceEntitlementException("Unexpected http response."); } @@ -171,16 +191,15 @@ public class EapAkaApi { @VisibleForTesting String entitlementStatusUrl( String appId, String serverUrl, ServiceEntitlementRequest request) { - TelephonyManager telephonyManager = - (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); - telephonyManager = telephonyManager.createForSubscriptionId(simSubscriptionId); + TelephonyManager telephonyManager = mContext.getSystemService( + TelephonyManager.class).createForSubscriptionId(mSimSubscriptionId); Uri.Builder urlBuilder = Uri.parse(serverUrl).buildUpon(); if (TextUtils.isEmpty(request.authenticationToken())) { // EAP_ID required for initial AuthN urlBuilder.appendQueryParameter( EAP_ID, getImsiEap(telephonyManager.getSimOperator(), - telephonyManager.getSubscriberId())); + telephonyManager.getSubscriberId())); } else { // IMSI and token required for fast AuthN. urlBuilder diff --git a/java/com/android/libraries/entitlement/eapaka/EapAkaResponse.java b/java/com/android/libraries/entitlement/eapaka/EapAkaResponse.java index 8f4f138..1d9be2b 100644 --- a/java/com/android/libraries/entitlement/eapaka/EapAkaResponse.java +++ b/java/com/android/libraries/entitlement/eapaka/EapAkaResponse.java @@ -22,6 +22,8 @@ import android.text.TextUtils; import android.util.Base64; import android.util.Log; +import androidx.annotation.Nullable; + import com.android.libraries.entitlement.ServiceEntitlementException; import com.android.libraries.entitlement.eapaka.utils.BytesConverter; @@ -33,9 +35,10 @@ import java.security.NoSuchAlgorithmException; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; -import androidx.annotation.Nullable; - -/** Generate the response of EAP-AKA token challenge for initial AUTN. */ +/** + * Generate the response of EAP-AKA token challenge. Refer to RFC 4187 Section 8.1 Message + * Format/RFC 3748 Session 4 EAP Packet Format. + */ class EapAkaResponse { private static final String TAG = "ServiceEntitlement"; @@ -49,45 +52,52 @@ class EapAkaResponse { private static final byte ATTRIBUTE_RES = 0x03; private static final byte ATTRIBUTE_MAC = 0x0B; private static final String ALGORITHM_HMAC_SHA1 = "HmacSHA1"; - private static final int RAND_LENGTH = 20; - private static final int AUTN_LENGTH = 20; + private static final int ATTRIBUTE_LENGTH = 20; private static final int SHA1_OUTPUT_LENGTH = 20; - /** RAND length 16. */ private static final byte RAND_LEN = 0x10; - /** AUTN length 16. */ private static final byte AUTN_LEN = 0x10; - /* 1 for Request, 2 for Response*/ - private byte code = -1; - /* The identifier of Response must same as Request */ - private byte identifier = -1; - /* The total length of full EAP-AKA message, include code, identifier, ... */ - private int length = -1; - /* In EAP-AKA, the Type field is set to 23 */ - private byte type = -1; - /* SubType for AKA-Challenge should be 1 */ - private byte subType = -1; - /* The value of AT_AUTN, network authentication token */ - private byte[] autn; - /* The value of AT_RAND, RAND random number*/ - private byte[] rand; - - private boolean valid; + /** + * {@link #CODE_REQUEST} for Request, {@link #CODE_RESPONSE} for Response + */ + private byte mCode = -1; + /** + * The identifier of Response must same as Request + */ + private byte mIdentifier = -1; + /** + * The total length of full EAP-AKA message, include code, identifier, ... + */ + private int mLength = -1; + /** + * In EAP-AKA, the Type field is set to {@link #TYPE_EAP_AKA} + */ + private byte mType = -1; + /** + * SubType for AKA-Challenge should be {@link #SUBTYPE_AKA_CHALLENGE} + */ + private byte mSubType = -1; + /** + * The value of AT_AUTN, network authentication token + */ + private byte[] mAutn; + /** + * The value of AT_RAND, RAND random number + */ + private byte[] mRand; - @VisibleForTesting - static String challengeResponseForTesting; + private boolean mValid; public EapAkaResponse(String eapAkaChallenge) { try { parseEapAkaChallengeRequest(eapAkaChallenge); } catch (Exception e) { Log.e(TAG, "parseEapAkaChallengeRequest Exception:", e); - valid = false; + mValid = false; } } - /** Refer to RFC 4187 Section 8.1 Message Format/RFC 3748 Session 4 EAP Packet Format. */ private void parseEapAkaChallengeRequest(String request) { if (TextUtils.isEmpty(request)) { return; @@ -96,7 +106,7 @@ class EapAkaResponse { try { byte[] data = Base64.decode(request, Base64.DEFAULT); if (parseEapAkaHeader(data) && parseRandAndAutn(data)) { - valid = true; + mValid = true; } else { Log.d(TAG, "Invalid data!"); } @@ -115,29 +125,29 @@ class EapAkaResponse { if (data.length <= EAP_AKA_HEADER_LENGTH) { return false; } - code = data[0]; - identifier = data[1]; - length = ((data[2] & 0xff) << 8) | (data[3] & 0xff); - type = data[4]; - subType = data[5]; + mCode = data[0]; + mIdentifier = data[1]; + mLength = ((data[2] & 0xff) << 8) | (data[3] & 0xff); + mType = data[4]; + mSubType = data[5]; // valid header - if (code != CODE_REQUEST - || length != data.length - || type != TYPE_EAP_AKA - || subType != SUBTYPE_AKA_CHALLENGE) { + if (mCode != CODE_REQUEST + || mLength != data.length + || mType != TYPE_EAP_AKA + || mSubType != SUBTYPE_AKA_CHALLENGE) { Log.d( TAG, "Invalid EAP-AKA Header, code=" - + code + + mCode + ", length=" - + length + + mLength + ", real length=" + data.length + ", type=" - + type + + mType + ", subType=" - + subType); + + mSubType); return false; } @@ -172,26 +182,26 @@ class EapAkaResponse { // see RFC 4187 section 11 for attribute type if (attributeType == ATTRIBUTE_RAND) { - if (length != RAND_LENGTH) { + if (length != ATTRIBUTE_LENGTH) { Log.d(TAG, "AT_RAND length is " + length); return false; } - rand = new byte[16]; - System.arraycopy(data, index + 4, rand, 0, 16); + mRand = new byte[16]; + System.arraycopy(data, index + 4, mRand, 0, 16); } else if (attributeType == ATTRIBUTE_AUTN) { - if (length != AUTN_LENGTH) { + if (length != ATTRIBUTE_LENGTH) { Log.d(TAG, "AT_AUTN length is " + length); return false; } - autn = new byte[16]; - System.arraycopy(data, index + 4, autn, 0, 16); + mAutn = new byte[16]; + System.arraycopy(data, index + 4, mAutn, 0, 16); } index += length; - } // while + } // check has AT_RAND and AT_AUTH - if (rand == null || autn == null) { + if (mRand == null || mAutn == null) { Log.d(TAG, "Invalid Type Datas!"); return false; } @@ -201,16 +211,11 @@ class EapAkaResponse { /** * Returns EAP-AKA challenge response message which generated with SIM EAP-AKA authentication - * with - * network provided EAP-AKA challenge request message. + * with network provided EAP-AKA challenge request message. */ public String getEapAkaChallengeResponse(Context context, int simSubscriptionId) throws ServiceEntitlementException { - if(challengeResponseForTesting != null) { - return challengeResponseForTesting; - } - - if (!valid) { + if (!mValid) { throw new ServiceEntitlementException("EAP-AKA Challenge message not valid!"); } @@ -230,8 +235,8 @@ class EapAkaResponse { // generate master key MasterKey mk = MasterKey.create( - EapAkaApi.getImsiEap(telephonyManager.getSubscriberId(), - telephonyManager.getSimOperator()), + EapAkaApi.getImsiEap(telephonyManager.getSimOperator(), + telephonyManager.getSubscriberId()), securityContext.getIk(), securityContext.getCk()); // K_aut is the key used to calculate MAC @@ -250,25 +255,30 @@ class EapAkaResponse { return Base64.encodeToString(challengeResponse, Base64.NO_WRAP).trim(); } - /** Returns Base64 encoded GSM/3G security context for SIM Authentication request. */ + /** + * Returns Base64 encoded GSM/3G security context for SIM Authentication request. + */ @Nullable private String getSimAuthChallengeData() { - if (!valid) { + if (!mValid) { return null; } byte[] challengeData = new byte[RAND_LEN + AUTN_LEN + 2]; challengeData[0] = RAND_LEN; - System.arraycopy(rand, 0, challengeData, 1, RAND_LEN); + System.arraycopy(mRand, 0, challengeData, 1, RAND_LEN); challengeData[RAND_LEN + 1] = AUTN_LEN; - System.arraycopy(autn, 0, challengeData, RAND_LEN + 2, AUTN_LEN); + System.arraycopy(mAutn, 0, challengeData, RAND_LEN + 2, AUTN_LEN); return Base64.encodeToString(challengeData, Base64.NO_WRAP).trim(); } - /** Returns EAP-AKA Challenge response message byte array data or null if failed to generate. */ + /** + * Returns EAP-AKA Challenge response message or {@code null} if failed to generate. + */ + @VisibleForTesting @Nullable - public byte[] generateEapAkaChallengeResponse(@Nullable byte[] res, byte[] aut) { + byte[] generateEapAkaChallengeResponse(@Nullable byte[] res, @Nullable byte[] aut) { if (res == null || aut == null) { return null; } @@ -301,7 +311,7 @@ class EapAkaResponse { // set up header message[0] = CODE_RESPONSE; // Identifier need to same with request - message[1] = identifier; + message[1] = mIdentifier; // length include entire EAP-AKA message byte[] lengthBytes = BytesConverter.convertIntegerTo4Bytes(message.length); message[2] = lengthBytes[2]; diff --git a/java/com/android/libraries/entitlement/eapaka/EapAkaSecurityContext.java b/java/com/android/libraries/entitlement/eapaka/EapAkaSecurityContext.java index bb451fd..925e258 100644 --- a/java/com/android/libraries/entitlement/eapaka/EapAkaSecurityContext.java +++ b/java/com/android/libraries/entitlement/eapaka/EapAkaSecurityContext.java @@ -23,7 +23,7 @@ import android.util.Log; import com.android.libraries.entitlement.ServiceEntitlementException; /** - * Provide format to handle request/response SIM Authentication with GSM/3G security context. + * Provides format to handle request/response SIM Authentication with GSM/3G security context. * * <p>Reference ETSI TS 131 102, Section 7.1.2.1 GSM/3G security context. */ @@ -32,19 +32,21 @@ class EapAkaSecurityContext { private static final byte RESPONSE_TAG_SUCCESS = (byte) 0xDB; - private boolean valid; + private boolean mValid; /* Authentication result from SIM */ - private byte[] res; + private byte[] mRes; /* Cipher Key */ - private byte[] ck; + private byte[] mCk; /* Integrity Key */ - private byte[] ik; + private byte[] mIk; private EapAkaSecurityContext() { } - /** Provide {@link EapAkaSecurityContext} from response data. */ + /** + * Provide {@link EapAkaSecurityContext} from response data. + */ public static EapAkaSecurityContext from(String response) throws ServiceEntitlementException { EapAkaSecurityContext securityContext = new EapAkaSecurityContext(); @@ -56,11 +58,9 @@ class EapAkaSecurityContext { } /** - * Parses SIM EAP-AKA Authentication responsed data and returns valid {@link - * EapAkaSecurityContext} - * for successful data; otherwise, returns invalid. + * Parses SIM EAP-AKA Authentication responded data. */ - void parseResponseData(String response) { + private void parseResponseData(String response) { if (TextUtils.isEmpty(response)) { Log.d(TAG, "parseResponseData but input empty data!"); return; @@ -84,27 +84,27 @@ class EapAkaSecurityContext { // Parse RES index++; // move to RES length byte - res = parseTag(index, data); - if (res == null) { + mRes = parseTag(index, data); + if (mRes == null) { Log.d(TAG, "Invalid data! can't parse RES!"); return; } // Parse CK - index += res.length + 1; // move to CK length byte - ck = parseTag(index, data); - if (ck == null) { + index += mRes.length + 1; // move to CK length byte + mCk = parseTag(index, data); + if (mCk == null) { Log.d(TAG, "Invalid data! can't parse CK!"); return; } // Parse IK - index += ck.length + 1; // move to IK length byte - ik = parseTag(index, data); - if (ik == null) { + index += mCk.length + 1; // move to IK length byte + mIk = parseTag(index, data); + if (mIk == null) { Log.d(TAG, "Invalid data! can't parse IK!"); return; } - valid = true; + mValid = true; } catch (IllegalArgumentException illegalArgumentException) { Log.e(TAG, "Invalid base-64 content"); } @@ -129,23 +129,31 @@ class EapAkaSecurityContext { return dest; } - /** Returns {@code valid}. */ + /** + * Returns {@code valid}. + */ boolean isValid() { - return valid; + return mValid; } - /** Returns {@code res}. */ + /** + * Returns {@code res}. + */ public byte[] getRes() { - return res; + return mRes; } - /** Returns {@code ck}. */ + /** + * Returns {@code ck}. + */ public byte[] getCk() { - return ck; + return mCk; } - /** Returns {@code ik}. */ + /** + * Returns {@code ik}. + */ public byte[] getIk() { - return ik; + return mIk; } } diff --git a/java/com/android/libraries/entitlement/eapaka/MasterKey.java b/java/com/android/libraries/entitlement/eapaka/MasterKey.java index 57b8ab3..d37894d 100644 --- a/java/com/android/libraries/entitlement/eapaka/MasterKey.java +++ b/java/com/android/libraries/entitlement/eapaka/MasterKey.java @@ -21,6 +21,8 @@ import static java.nio.charset.StandardCharsets.UTF_8; import android.text.TextUtils; import android.util.Log; +import androidx.annotation.Nullable; + import com.android.libraries.entitlement.ServiceEntitlementException; import com.android.libraries.entitlement.eapaka.utils.BytesConverter; @@ -28,8 +30,6 @@ import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import androidx.annotation.Nullable; - /** * The class for Master Key. * @@ -49,13 +49,13 @@ class MasterKey { private static final int LENGTH_TEKS = 160; /* Master Key */ - private byte[] masterKey; + private byte[] mMasterKey; /* Transient EAP Keys */ - private byte[] encr; - private byte[] aut; - private byte[] msk; - private byte[] emsk; + private byte[] mEncr; + private byte[] mAut; + private byte[] mMsk; + private byte[] mEmsk; private MasterKey() { } @@ -91,7 +91,7 @@ class MasterKey { try { MessageDigest messageDigest = MessageDigest.getInstance("SHA-1"); messageDigest.update(data); - masterKey = messageDigest.digest(); + mMasterKey = messageDigest.digest(); } catch (NoSuchAlgorithmException e) { Log.d(TAG, "process SHA-1 failed", e); } @@ -118,29 +118,29 @@ class MasterKey { } int index = 0; - encr = new byte[LENGTH_K_ENCR]; - System.arraycopy(teks, index, encr, 0, LENGTH_K_ENCR); + mEncr = new byte[LENGTH_K_ENCR]; + System.arraycopy(teks, index, mEncr, 0, LENGTH_K_ENCR); index += LENGTH_K_ENCR; - aut = new byte[LENGTH_K_AUT]; - System.arraycopy(teks, index, aut, 0, LENGTH_K_AUT); + mAut = new byte[LENGTH_K_AUT]; + System.arraycopy(teks, index, mAut, 0, LENGTH_K_AUT); index += LENGTH_K_AUT; - msk = new byte[LENGTH_MSK]; - System.arraycopy(teks, index, msk, 0, LENGTH_MSK); + mMsk = new byte[LENGTH_MSK]; + System.arraycopy(teks, index, mMsk, 0, LENGTH_MSK); index += LENGTH_MSK; - emsk = new byte[LENGTH_EMSK]; - System.arraycopy(teks, index, emsk, 0, LENGTH_EMSK); + mEmsk = new byte[LENGTH_EMSK]; + System.arraycopy(teks, index, mEmsk, 0, LENGTH_EMSK); } /** Returns {@code aut}. */ public byte[] getAut() { - return aut; + return mAut; } // RFC 4187 Appendix A. Pseudo-Random Number Generator @Nullable private byte[] generatePsudoRandomNumber() { // Step 1: Choose a new, secret value for the seed-key, XKEY - byte[] key = masterKey; + byte[] key = mMasterKey; // 160-bit XKEY and XVAL values are used, so b = 160. On each full // authentication, the Master Key is used as the initial secret seed-key diff --git a/java/com/android/libraries/entitlement/http/HttpClient.java b/java/com/android/libraries/entitlement/http/HttpClient.java index 79f42ff..9d102ce 100644 --- a/java/com/android/libraries/entitlement/http/HttpClient.java +++ b/java/com/android/libraries/entitlement/http/HttpClient.java @@ -26,6 +26,8 @@ import static java.util.concurrent.TimeUnit.SECONDS; import android.text.TextUtils; import android.util.Log; +import androidx.annotation.WorkerThread; + import com.android.libraries.entitlement.ServiceEntitlementException; import com.android.libraries.entitlement.http.HttpConstants.ContentType; import com.android.libraries.entitlement.utils.StreamUtils; @@ -39,9 +41,9 @@ import java.net.URL; import java.net.URLConnection; import java.util.Map; -import androidx.annotation.WorkerThread; - -/** Implement the HTTP request method according to TS.43 specification. */ +/** + * Implement the HTTP request method according to TS.43 specification. + */ public class HttpClient { private static final String TAG = "ServiceEntitlement"; private static final boolean DEBUG = false; // STOPSHIP if true @@ -49,7 +51,7 @@ public class HttpClient { private static final int SOCKET_TIMEOUT_VALUE = (int) SECONDS.toMillis(30); private static final int CONNECT_TIMEOUT_VALUE = (int) SECONDS.toMillis(30); - private HttpURLConnection connection; + private HttpURLConnection mConnection; @WorkerThread // TODO(b/177544547): Add debug messages @@ -57,23 +59,23 @@ public class HttpClient { try { logd("HttpClient.request url: " + request.url()); createConnection(request); - if (connection == null) { + if (mConnection == null) { logd("HttpClient.request connection is null"); throw new ServiceEntitlementException("No connection"); } - logd("HttpClient.request headers (partial): " + connection.getRequestProperties()); + logd("HttpClient.request headers (partial): " + mConnection.getRequestProperties()); if (POST.equals(request.requestMethod())) { - try (OutputStream out = new DataOutputStream(connection.getOutputStream())) { + try (OutputStream out = new DataOutputStream(mConnection.getOutputStream())) { out.write(request.postData().toString().getBytes(UTF_8)); logd("HttpClient.request post data: " + request.postData()); } } - connection.connect(); // This is to trigger SocketTimeoutException early - HttpResponse response = getHttpResponse(connection); + mConnection.connect(); // This is to trigger SocketTimeoutException early + HttpResponse response = getHttpResponse(mConnection); Log.d(TAG, "HttpClient.response : " + response); return response; } catch (IOException e) { - InputStream errorStream = connection.getErrorStream(); + InputStream errorStream = mConnection.getErrorStream(); Log.e( TAG, "HttpClient.request() error: " + StreamUtils.inputStreamToStringSafe( @@ -87,19 +89,19 @@ public class HttpClient { private void createConnection(HttpRequest request) throws ServiceEntitlementException { try { URL url = new URL(request.url()); - connection = (HttpURLConnection) url.openConnection(); + mConnection = (HttpURLConnection) url.openConnection(); // add HTTP headers for (Map.Entry<String, String> entry : request.requestProperties().entrySet()) { - connection.addRequestProperty(entry.getKey(), entry.getValue()); + mConnection.addRequestProperty(entry.getKey(), entry.getValue()); } // set parameters - connection.setRequestMethod(request.requestMethod()); - connection.setConnectTimeout(CONNECT_TIMEOUT_VALUE); - connection.setReadTimeout(SOCKET_TIMEOUT_VALUE); + mConnection.setRequestMethod(request.requestMethod()); + mConnection.setConnectTimeout(CONNECT_TIMEOUT_VALUE); + mConnection.setReadTimeout(SOCKET_TIMEOUT_VALUE); if (POST.equals(request.requestMethod())) { - connection.setDoOutput(true); + mConnection.setDoOutput(true); } } catch (IOException e) { Log.e(TAG, "IOException: " + e.getMessage()); @@ -108,9 +110,9 @@ public class HttpClient { } private void closeConnection() { - if (connection != null) { - connection.disconnect(); - connection = null; + if (mConnection != null) { + mConnection.disconnect(); + mConnection = null; } } @@ -121,8 +123,9 @@ public class HttpClient { logd("HttpClient.response headers: " + connection.getHeaderFields()); if (responseCode != HttpURLConnection.HTTP_OK) { throw new ServiceEntitlementException( - ServiceEntitlementException.ERROR_HTTP_STATUS_NOT_SUCCESS, responseCode, null, - "Invalid connection response", null); + ServiceEntitlementException.ERROR_HTTP_STATUS_NOT_SUCCESS, responseCode, + null, + "Invalid connection response", null); } String responseBody = readResponse(connection); logd("HttpClient.response body: " + responseBody); @@ -134,8 +137,8 @@ public class HttpClient { .build(); } catch (IOException e) { throw new ServiceEntitlementException( - ServiceEntitlementException.ERROR_HTTP_STATUS_NOT_SUCCESS, 0, null, - "Read response failed!", e); + ServiceEntitlementException.ERROR_HTTP_STATUS_NOT_SUCCESS, 0, null, + "Read response failed!", e); } } diff --git a/java/com/android/libraries/entitlement/http/HttpConstants.java b/java/com/android/libraries/entitlement/http/HttpConstants.java index c4ed5e2..58f8c48 100644 --- a/java/com/android/libraries/entitlement/http/HttpConstants.java +++ b/java/com/android/libraries/entitlement/http/HttpConstants.java @@ -16,21 +16,30 @@ package com.android.libraries.entitlement.http; -/** Http constants using for entitlement flow of TS.43. */ +/** + * Http constants using for entitlement flow of TS.43. + */ public final class HttpConstants { - private HttpConstants() {} + private HttpConstants() { + } - /** Possible request methods for Entitlement server response. */ + /** + * Possible request methods for Entitlement server response. + */ public static final class RequestMethod { - private RequestMethod() {} + private RequestMethod() { + } public static final String GET = "GET"; public static final String POST = "POST"; } - /** Possible content type for Entitlement server response. */ + /** + * Possible content type for Entitlement server response. + */ public static final class ContentType { - private ContentType() {} + private ContentType() { + } public static final int UNKNOWN = -1; public static final int JSON = 0; diff --git a/java/com/android/libraries/entitlement/http/HttpRequest.java b/java/com/android/libraries/entitlement/http/HttpRequest.java index af81b10..733355f 100644 --- a/java/com/android/libraries/entitlement/http/HttpRequest.java +++ b/java/com/android/libraries/entitlement/http/HttpRequest.java @@ -21,14 +21,15 @@ import android.util.ArrayMap; import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableMap; -import java.util.Map; - import org.json.JSONObject; -/** The parameters of the http request. */ +import java.util.Map; + +/** + * The parameters of the http request. + */ @AutoValue public abstract class HttpRequest { - public abstract String url(); public abstract String requestMethod(); @@ -39,7 +40,9 @@ public abstract class HttpRequest { public abstract ImmutableMap<String, String> requestProperties(); - /** Builder of {@link HttpRequest}. */ + /** + * Builder of {@link HttpRequest}. + */ @AutoValue.Builder public abstract static class Builder { diff --git a/java/com/android/libraries/entitlement/http/HttpResponse.java b/java/com/android/libraries/entitlement/http/HttpResponse.java index 1cb165e..b6ca35f 100644 --- a/java/com/android/libraries/entitlement/http/HttpResponse.java +++ b/java/com/android/libraries/entitlement/http/HttpResponse.java @@ -20,11 +20,14 @@ import com.android.libraries.entitlement.http.HttpConstants.ContentType; import com.google.auto.value.AutoValue; -/** The response of the http request. */ +/** + * The response of the http request. + */ @AutoValue public abstract class HttpResponse { - - /** Content type of the response. */ + /** + * Content type of the response. + */ public abstract int contentType(); public abstract String body(); @@ -33,7 +36,9 @@ public abstract class HttpResponse { public abstract String responseMessage(); - /** Builder of {@link HttpResponse}. */ + /** + * Builder of {@link HttpResponse}. + */ @AutoValue.Builder public abstract static class Builder { diff --git a/java/com/android/libraries/entitlement/utils/BytesConverter.java b/java/com/android/libraries/entitlement/utils/BytesConverter.java index 034ac9c..3ec1df2 100644 --- a/java/com/android/libraries/entitlement/utils/BytesConverter.java +++ b/java/com/android/libraries/entitlement/utils/BytesConverter.java @@ -16,10 +16,10 @@ package com.android.libraries.entitlement.eapaka.utils; -import java.nio.ByteBuffer; - import androidx.annotation.Nullable; +import java.nio.ByteBuffer; + public class BytesConverter { private static final int INTEGER_SIZE = 4; // 4 bytes @@ -53,7 +53,9 @@ public class BytesConverter { return ret.toString(); } - /** Converts integer to 4 bytes. */ + /** + * Converts integer to 4 bytes. + */ public static byte[] convertIntegerTo4Bytes(int value) { return ByteBuffer.allocate(INTEGER_SIZE).putInt(value).array(); } diff --git a/java/com/android/libraries/entitlement/utils/StreamUtils.java b/java/com/android/libraries/entitlement/utils/StreamUtils.java index 1ff2ffc..e2ba890 100644 --- a/java/com/android/libraries/entitlement/utils/StreamUtils.java +++ b/java/com/android/libraries/entitlement/utils/StreamUtils.java @@ -22,15 +22,18 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -/** Utility methods about InputStream. */ +/** + * Utility methods about InputStream. + */ public final class StreamUtils { - private static final int BUFFER_SIZE = 1024; private StreamUtils() { } - /** Reads an {@link InputStream} into a string. */ + /** + * Reads an {@link InputStream} into a string. + */ public static String inputStreamToString(InputStream inputStream) throws IOException { try (BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream); ByteArrayOutputStream result = new ByteArrayOutputStream()) { @@ -43,7 +46,9 @@ public final class StreamUtils { } } - /** Reads an {@link InputStream} into a string. Returns an empty string if any error. */ + /** + * Reads an {@link InputStream} into a string. Returns an empty string if any error. + */ public static String inputStreamToStringSafe(InputStream inputStream) { try { return inputStreamToString(inputStream); |