chromium/components/webauthn/android/internal_authenticator_android.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/webauthn/android/internal_authenticator_android.h"

#include <string>
#include <utility>
#include <vector>

#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_bytebuffer.h"
#include "base/android/jni_string.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "url/origin.h"

// Must come after all headers that specialize FromJniType() / ToJniType().
#include "components/webauthn/android/jni_headers/InternalAuthenticator_jni.h"

using base::android::AttachCurrentThread;
using base::android::ConvertUTF8ToJavaString;
using base::android::JavaArrayOfByteArrayToBytesVector;
using base::android::JavaRef;
using base::android::ScopedJavaLocalRef;
using base::android::ToJavaArrayOfByteArray;

namespace webauthn {

InternalAuthenticatorAndroid::InternalAuthenticatorAndroid(
    content::RenderFrameHost* render_frame_host)
    : render_frame_host_id_(render_frame_host->GetGlobalId()) {
  JNIEnv* env = AttachCurrentThread();
  java_internal_authenticator_ref_ = Java_InternalAuthenticator_create(
      env, reinterpret_cast<intptr_t>(this),
      render_frame_host->GetJavaRenderFrameHost());
}

InternalAuthenticatorAndroid::~InternalAuthenticatorAndroid() {
  JNIEnv* env = AttachCurrentThread();
  DCHECK(!java_internal_authenticator_ref_.is_null());
  Java_InternalAuthenticator_clearNativePtr(env,
                                            java_internal_authenticator_ref_);
}

void InternalAuthenticatorAndroid::SetEffectiveOrigin(
    const url::Origin& origin) {
  JNIEnv* env = AttachCurrentThread();
  JavaRef<jobject>& obj = GetJavaObject();
  DCHECK(!obj.is_null());

  Java_InternalAuthenticator_setEffectiveOrigin(env, obj,
                                                origin.ToJavaObject());
}

void InternalAuthenticatorAndroid::SetPaymentOptions(
    blink::mojom::PaymentOptionsPtr payment) {
  JNIEnv* env = AttachCurrentThread();
  JavaRef<jobject>& obj = GetJavaObject();
  DCHECK(!obj.is_null());

  std::vector<uint8_t> byte_vector =
      blink::mojom::PaymentOptions::Serialize(&payment);
  ScopedJavaLocalRef<jobject> byte_buffer = ScopedJavaLocalRef<jobject>(
      env, env->NewDirectByteBuffer(byte_vector.data(), byte_vector.size()));
  base::android::CheckException(env);

  Java_InternalAuthenticator_setPaymentOptions(env, obj, byte_buffer);
}

void InternalAuthenticatorAndroid::MakeCredential(
    blink::mojom::PublicKeyCredentialCreationOptionsPtr options,
    blink::mojom::Authenticator::MakeCredentialCallback callback) {
  JNIEnv* env = AttachCurrentThread();
  JavaRef<jobject>& obj = GetJavaObject();
  DCHECK(!obj.is_null());

  make_credential_response_callback_ = std::move(callback);

  std::vector<uint8_t> byte_vector =
      blink::mojom::PublicKeyCredentialCreationOptions::Serialize(&options);
  ScopedJavaLocalRef<jobject> byte_buffer = ScopedJavaLocalRef<jobject>(
      env, env->NewDirectByteBuffer(byte_vector.data(), byte_vector.size()));
  base::android::CheckException(env);

  Java_InternalAuthenticator_makeCredential(env, obj, byte_buffer);
}

void InternalAuthenticatorAndroid::GetAssertion(
    blink::mojom::PublicKeyCredentialRequestOptionsPtr options,
    blink::mojom::Authenticator::GetAssertionCallback callback) {
  JNIEnv* env = AttachCurrentThread();
  JavaRef<jobject>& obj = GetJavaObject();
  DCHECK(!obj.is_null());

  get_assertion_response_callback_ = std::move(callback);

  std::vector<uint8_t> byte_vector =
      blink::mojom::PublicKeyCredentialRequestOptions::Serialize(&options);
  ScopedJavaLocalRef<jobject> byte_buffer = ScopedJavaLocalRef<jobject>(
      env, env->NewDirectByteBuffer(byte_vector.data(), byte_vector.size()));
  base::android::CheckException(env);

  Java_InternalAuthenticator_getAssertion(env, obj, byte_buffer);
}

void InternalAuthenticatorAndroid::
    IsUserVerifyingPlatformAuthenticatorAvailable(
        blink::mojom::Authenticator::
            IsUserVerifyingPlatformAuthenticatorAvailableCallback callback) {
  JNIEnv* env = AttachCurrentThread();
  JavaRef<jobject>& obj = GetJavaObject();
  DCHECK(!obj.is_null());

  is_uvpaa_callback_ = std::move(callback);
  Java_InternalAuthenticator_isUserVerifyingPlatformAuthenticatorAvailable(env,
                                                                           obj);
}

bool InternalAuthenticatorAndroid::IsGetMatchingCredentialIdsSupported() {
  JNIEnv* env = AttachCurrentThread();
  JavaRef<jobject>& obj = GetJavaObject();
  DCHECK(!obj.is_null());

  return Java_InternalAuthenticator_isGetMatchingCredentialIdsSupported(env,
                                                                        obj);
}

void InternalAuthenticatorAndroid::GetMatchingCredentialIds(
    const std::string& relying_party_id,
    const std::vector<std::vector<uint8_t>>& credential_ids,
    bool require_third_party_payment_bit,
    webauthn::GetMatchingCredentialIdsCallback callback) {
  JNIEnv* env = AttachCurrentThread();
  JavaRef<jobject>& obj = GetJavaObject();
  DCHECK(!obj.is_null());

  get_matching_credential_ids_callback_ = std::move(callback);
  Java_InternalAuthenticator_getMatchingCredentialIds(
      env, obj, ConvertUTF8ToJavaString(env, relying_party_id),
      ToJavaArrayOfByteArray(env, std::move(credential_ids)),
      require_third_party_payment_bit);
}

void InternalAuthenticatorAndroid::Cancel() {
  JNIEnv* env = AttachCurrentThread();
  JavaRef<jobject>& obj = GetJavaObject();
  DCHECK(!obj.is_null());

  Java_InternalAuthenticator_cancel(env, obj);
}

content::RenderFrameHost* InternalAuthenticatorAndroid::GetRenderFrameHost() {
  return content::RenderFrameHost::FromID(render_frame_host_id_);
}

void InternalAuthenticatorAndroid::InvokeMakeCredentialResponse(
    JNIEnv* env,
    jint status,
    const base::android::JavaParamRef<jobject>& byte_buffer) {
  blink::mojom::MakeCredentialAuthenticatorResponsePtr response;

  // |byte_buffer| may be null if authentication failed.
  if (byte_buffer) {
    auto span = base::android::JavaByteBufferToSpan(env, byte_buffer.obj());
    blink::mojom::MakeCredentialAuthenticatorResponse::Deserialize(
        span.data(), span.size(), &response);
  }

  DCHECK_NE(
      status,
      static_cast<int>(
          blink::mojom::AuthenticatorStatus::ERROR_WITH_DOM_EXCEPTION_DETAILS));
  std::move(make_credential_response_callback_)
      .Run(static_cast<blink::mojom::AuthenticatorStatus>(status),
           std::move(response), /*dom_exception_details=*/nullptr);
}

void InternalAuthenticatorAndroid::InvokeGetAssertionResponse(
    JNIEnv* env,
    jint status,
    const base::android::JavaParamRef<jobject>& byte_buffer) {
  blink::mojom::GetAssertionAuthenticatorResponsePtr response;

  // |byte_buffer| may be null if authentication failed.
  if (byte_buffer) {
    auto span = base::android::JavaByteBufferToSpan(env, byte_buffer.obj());
    blink::mojom::GetAssertionAuthenticatorResponse::Deserialize(
        span.data(), span.size(), &response);
  }

  DCHECK_NE(
      status,
      static_cast<int>(
          blink::mojom::AuthenticatorStatus::ERROR_WITH_DOM_EXCEPTION_DETAILS));
  std::move(get_assertion_response_callback_)
      .Run(static_cast<blink::mojom::AuthenticatorStatus>(status),
           std::move(response), /*dom_exception_details=*/nullptr);
}

void InternalAuthenticatorAndroid::
    InvokeIsUserVerifyingPlatformAuthenticatorAvailableResponse(
        JNIEnv* env,
        jboolean is_uvpaa) {
  std::move(is_uvpaa_callback_).Run(static_cast<bool>(is_uvpaa));
}

void InternalAuthenticatorAndroid::InvokeGetMatchingCredentialIdsResponse(
    JNIEnv* env,
    const base::android::JavaParamRef<jobjectArray>& credential_ids_array) {
  std::vector<std::vector<uint8_t>> credential_ids;
  JavaArrayOfByteArrayToBytesVector(env, credential_ids_array, &credential_ids);
  std::move(get_matching_credential_ids_callback_)
      .Run(std::move(credential_ids));
}

JavaRef<jobject>& InternalAuthenticatorAndroid::GetJavaObject() {
  if (java_internal_authenticator_ref_.is_null()) {
    JNIEnv* env = AttachCurrentThread();
    java_internal_authenticator_ref_ = Java_InternalAuthenticator_create(
        env, reinterpret_cast<intptr_t>(this),
        GetRenderFrameHost()->GetJavaRenderFrameHost());
  }
  return java_internal_authenticator_ref_;
}

}  // namespace webauthn