I got an issue with google safetynet api attestation response. Even if i supplied the safetynet client, nonce and the apikey to the "client.AttestAsync()" method it won't return SafetyNetApiAttestationResponse. What could possibly wrong with below code? How to get attestation response to check whether android device is run on emulator or rooted device in xamarin forms?
protected override void OnCreate(Bundle bundle)
{
Instance = this;
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
BackgroundAggregator.Init(this);
base.OnCreate(bundle);
DependencyService.Register<ToastNotification>();
ToastNotification.Init(this);
Rg.Plugins.Popup.Popup.Init(this);
global::Xamarin.Forms.Forms.Init(this, bundle);
micService = DependencyService.Resolve<IMicrophoneService>();
if (GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(this, 13000000) == ConnectionResult.Success)
{
SafetyNetClient client = Android.Gms.SafetyNet.SafetyNetClass.GetClient(this);
byte[] nonce = Android.Gms.SafetyNet.Nonce.Generate();
bool valid = Task.Run(() => RunSafetyNetCheck(client, nonce)).Result;
if (!valid)
LoadApplication(new App());
else
Toast.MakeText(this, "Failed response validation with Google!", ToastLength.Short).Show();
}
else
{
Toast.MakeText(this, "Update Google Play services.!", ToastLength.Short).Show();
}
}
private async Task<bool> RunSafetyNetCheck(SafetyNetClient client, byte[] nonce)
{
bool valid = false;
SafetyNetApiAttestationResponse r = await client.AttestAsync(nonce, apiKey);
if (r != null && !string.IsNullOrEmpty(r.JwsResult))
{
attestationResponse = r;
var decodedResult = r.DecodeJwsResult(nonce);
string error = null;
if (VerifyAttestResponse(decodedResult, nonce, out error))
{
valid = await attestationResponse.ValidateWithGoogle(apiKey);
}
else
{
Toast.MakeText(this, "Compatibility Failed: " + error, ToastLength.Long).Show();
}
}
else
{
Toast.MakeText(this, "Failed to Check Compatibility", ToastLength.Long).Show();
}
return valid;
}
private bool VerifyAttestResponse(string data, byte[] sentNonce, out string errorMessage)
{
errorMessage = null;
var json = JsonObject.Parse(data);
var error = GetValue(json, "error");
var nonce = GetValue(json, "nonce");
var timestampMs = GetValue(json, "timestampMs");
var apkPackageName = GetValue(json, "apkPackageName");
var apkCertDigestSha256 = GetValue(json, "apkCertificateDigestSha256");
var apkDigestSha256 = GetValue(json, "apkDigestSha256");
var ctsProfileMatch = GetValue(json, "ctsProfileMatch") == "true";
if (!string.IsNullOrEmpty(error))
{
errorMessage = "Response Contained an Error: " + error;
return false;
}
var sentNonceStr = Convert.ToBase64String(sentNonce);
if (!nonce.Equals(sentNonceStr))
{
errorMessage = "Nonce's do no match";
return false;
}
if (PackageName != apkPackageName)
{
errorMessage = "Package Names do not match";
return false;
}
if (!ctsProfileMatch)
{
errorMessage = "CTS Profile was false";
return false;
}
return true;
}
private string GetValue(JsonValue json, string field)
{
if (!json.ContainsKey(field))
return string.Empty;
return json[field].ToString().Trim('"');
}
I made my own sample here and cannot reproduce the behavior you see. You can find the full code in the following repository: https://github.com/Cheesebaron/Xamarin-SafetyNet
The relevant parts are in my Activity I do:
Inside of my safetynet helper I do the following:
Notice all calls have
.ConfigureAwait(false)
. We don't need to switch contexts all the time.This works fine and loads the attestation as expected.