Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.google.android.gms.common.Feature
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.common.api.CommonStatusCodes
import com.google.android.gms.common.api.Status
import com.google.android.gms.common.internal.ConnectionInfo
Expand Down Expand Up @@ -128,11 +129,12 @@ class RecaptchaServiceImpl(
}
} catch (e: Exception) {
Log.w(TAG, e)
val status = (e as? ApiException)?.status ?: Status.INTERNAL_ERROR
try {
if (params.version == LEGACY_VERSION) {
callback.onHandle(Status.INTERNAL_ERROR, null)
callback.onHandle(status, null)
} else {
callback.onResults(Status.INTERNAL_ERROR, InitResults())
callback.onResults(status, InitResults())
}
} catch (e: Exception) {
// Ignored
Expand All @@ -157,11 +159,12 @@ class RecaptchaServiceImpl(
}
} catch (e: Exception) {
Log.w(TAG, e)
val status = (e as? ApiException)?.status ?: Status.INTERNAL_ERROR
try {
if (params.version == LEGACY_VERSION) {
callback.onData(Status.INTERNAL_ERROR, null)
callback.onData(status, null)
} else {
callback.onResults(Status.INTERNAL_ERROR, ExecuteResults())
callback.onResults(status, ExecuteResults())
}
} catch (e: Exception) {
// Ignored
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.webkit.WebViewClientCompat
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.common.api.Status
import com.google.android.gms.recaptcha.RecaptchaHandle
import com.google.android.gms.recaptcha.RecaptchaResultData
import com.google.android.gms.recaptcha.RecaptchaStatusCodes
import com.google.android.gms.recaptcha.internal.ExecuteParams
import com.google.android.gms.recaptcha.internal.InitParams
import com.google.android.gms.tasks.Task
Expand Down Expand Up @@ -131,20 +134,28 @@ class RecaptchaWebImpl(private val context: Context, private val packageName: St
additionalArgs[key] = params.action.additionalArgs.getString(key)!!
}
val request = RecaptchaExecuteRequest(token = lastRequestToken, action = params.action.toString(), additionalArgs = additionalArgs).encode().toBase64(Base64.URL_SAFE, Base64.NO_WRAP)
val token = suspendCoroutine { continuation ->
executeFinished.set(false)
executeContinuation = continuation
eval("recaptcha.m.Main.execute(\"${request}\")")
lifecycleScope.launch {
delay(10000)
if (!executeFinished.getAndSet(true)) {
try {
continuation.resumeWithException(RuntimeException("Timeout reached"))
} catch (_: Exception) {}
try {
val token = suspendCoroutine { continuation ->
executeFinished.set(false)
executeContinuation = continuation
eval("recaptcha.m.Main.execute(\"${request}\")")
lifecycleScope.launch {
delay(10000)
if (!executeFinished.getAndSet(true)) {
try {
continuation.resumeWithException(RuntimeException("Timeout reached"))
} catch (_: Exception) {}
}
}
}
return RecaptchaResultData(token)
} catch (e: Exception) {
Log.w(TAG, "Exception during Recaptcha execute", e)
webView?.stopLoading()
webView?.loadUrl("about:blank")
webView = null
throw ApiException(Status(RecaptchaStatusCodes.RECAPTCHA_FEATURE_OFF, "Recaptcha execution failed: ${e.message}"))
}
return RecaptchaResultData(token)
}

override suspend fun close(handle: RecaptchaHandle): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,18 +166,23 @@ private class IntegrityServiceImpl(private val context: Context, override val li
}
Log.d(TAG, "requestIntegrityToken authToken: $authToken")

val droidGuardData = withContext(Dispatchers.IO) {
val droidGuardResultsRequest = DroidGuardResultsRequest()
droidGuardResultsRequest.bundle.putString("thirdPartyCallerAppPackageName", packageName)
Log.d(TAG, "Running DroidGuard (flow: $INTEGRITY_FLOW_NAME, data: $data)")
val droidGuardToken = DroidGuard.getClient(context).getResults(INTEGRITY_FLOW_NAME, data, droidGuardResultsRequest).await()
Log.d(TAG, "Running DroidGuard (flow: $INTEGRITY_FLOW_NAME, droidGuardToken: $droidGuardToken)")
Base64.decode(droidGuardToken, Base64.NO_PADDING or Base64.NO_WRAP or Base64.URL_SAFE).toByteString()
val droidGuardData = try {
withContext(Dispatchers.IO) {
val droidGuardResultsRequest = DroidGuardResultsRequest()
droidGuardResultsRequest.bundle.putString("thirdPartyCallerAppPackageName", packageName)
Log.d(TAG, "Running DroidGuard (flow: $INTEGRITY_FLOW_NAME, data: $data)")
val droidGuardToken = DroidGuard.getClient(context).getResults(INTEGRITY_FLOW_NAME, data, droidGuardResultsRequest).await()
Log.d(TAG, "Running DroidGuard (flow: $INTEGRITY_FLOW_NAME, droidGuardToken: $droidGuardToken)")
Base64.decode(droidGuardToken, Base64.NO_PADDING or Base64.NO_WRAP or Base64.URL_SAFE).toByteString()
}
} catch (e: Exception) {
Log.e(TAG, "DroidGuard execution failed", e)
throw StandardIntegrityException(IntegrityErrorCode.CANNOT_BIND_TO_SERVICE, "DroidGuard binding or execution failed: ${e.message}")
}

if (droidGuardData.utf8().startsWith(INTEGRITY_PREFIX_ERROR)) {
Log.w(TAG, "droidGuardData: ${droidGuardData.utf8()}")
throw StandardIntegrityException(IntegrityErrorCode.NETWORK_ERROR, "DroidGuard failed.")
Log.w(TAG, "droidGuardData error: ${droidGuardData.utf8()}")
throw StandardIntegrityException(IntegrityErrorCode.CANNOT_BIND_TO_SERVICE, "DroidGuard returned error: ${droidGuardData.utf8()}")
}

val integrityRequest = IntegrityRequest(
Expand All @@ -203,9 +208,9 @@ private class IntegrityServiceImpl(private val context: Context, override val li
val integrityToken = integrityResponse.contentWrapper?.content?.token
if (integrityToken.isNullOrEmpty()) {
if (integrityResponse.integrityResponseError?.error != null) {
throw StandardIntegrityException(IntegrityErrorCode.INTERNAL_ERROR, integrityResponse.integrityResponseError.error)
throw StandardIntegrityException(IntegrityErrorCode.API_NOT_AVAILABLE, integrityResponse.integrityResponseError.error)
}
throw StandardIntegrityException(IntegrityErrorCode.INTERNAL_ERROR, "No token in response.")
throw StandardIntegrityException(IntegrityErrorCode.API_NOT_AVAILABLE, "No token in response.")
}

Log.d(TAG, "requestIntegrityToken integrityToken: $integrityToken")
Expand All @@ -214,7 +219,8 @@ private class IntegrityServiceImpl(private val context: Context, override val li
}.onFailure {
Log.w(TAG, "requestIntegrityToken has exception: ", it)
integrityData?.updateAppIntegrityContent(context, System.currentTimeMillis(), "Integrity check failed: ${it.message}")
callback.onError(integrityData?.packageName, IntegrityErrorCode.INTERNAL_ERROR, it.message ?: "Exception")
val errorCode = (it as? StandardIntegrityException)?.code ?: IntegrityErrorCode.INTERNAL_ERROR
callback.onError(integrityData?.packageName ?: packageName, errorCode, it.message ?: "Exception")
}
}
}
Expand Down