// Copyright 2020 Google LLC
//
// 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.
//
///////////////////////////////////////////////////////////////////////////////

// Implementation of a Keyset Service.
#include "keyset_impl.h"

#include <ostream>
#include <memory>
#include <sstream>
#include <string>
#include <utility>

#include "tink/aead/aead_key_templates.h"
#include "tink/binary_keyset_reader.h"
#include "tink/binary_keyset_writer.h"
#include "tink/cleartext_keyset_handle.h"
#include "tink/daead/deterministic_aead_key_templates.h"
#include "tink/hybrid/hybrid_key_templates.h"
#include "tink/json_keyset_reader.h"
#include "tink/json_keyset_writer.h"
#include "tink/jwt/jwt_key_templates.h"
#include "tink/keyset_handle.h"
#include "tink/mac/mac_key_templates.h"
#include "tink/prf/prf_key_templates.h"
#include "tink/signature/signature_key_templates.h"
#include "tink/streamingaead/streaming_aead_key_templates.h"
#include "proto/tink.pb.h"

namespace tink_testing_api {

using ::crypto::tink::BinaryKeysetReader;
using ::crypto::tink::BinaryKeysetWriter;
using ::crypto::tink::CleartextKeysetHandle;
using ::crypto::tink::JsonKeysetReader;
using ::crypto::tink::JsonKeysetWriter;
using ::crypto::tink::KeysetHandle;
using ::crypto::tink::KeysetReader;
using ::crypto::tink::KeysetWriter;
using ::crypto::tink::util::StatusOr;
using ::google::crypto::tink::KeyTemplate;

KeysetImpl::KeysetImpl() {
  key_templates_["AES128_EAX"] = crypto::tink::AeadKeyTemplates::Aes128Eax();
  key_templates_["AES256_EAX"] = crypto::tink::AeadKeyTemplates::Aes256Eax();
  key_templates_["AES128_GCM"] = crypto::tink::AeadKeyTemplates::Aes128Gcm();
  key_templates_["AES128_GCM_RAW"] =
      crypto::tink::AeadKeyTemplates::Aes128GcmNoPrefix();
  key_templates_["AES256_GCM"] = crypto::tink::AeadKeyTemplates::Aes256Gcm();
  key_templates_["AES256_GCM_RAW"] =
      crypto::tink::AeadKeyTemplates::Aes256GcmNoPrefix();
  key_templates_["AES128_GCM_SIV"] =
      crypto::tink::AeadKeyTemplates::Aes128GcmSiv();
  key_templates_["AES256_GCM_SIV"] =
      crypto::tink::AeadKeyTemplates::Aes256GcmSiv();
  key_templates_["AES128_CTR_HMAC_SHA256"] =
      crypto::tink::AeadKeyTemplates::Aes128CtrHmacSha256();
  key_templates_["AES256_CTR_HMAC_SHA256"] =
      crypto::tink::AeadKeyTemplates::Aes256CtrHmacSha256();
  key_templates_["CHACHA20_POLY1305"] =
      crypto::tink::AeadKeyTemplates::XChaCha20Poly1305();
  key_templates_["XCHACHA20_POLY1305"] =
      crypto::tink::AeadKeyTemplates::XChaCha20Poly1305();
  key_templates_["AES256_SIV"] =
      crypto::tink::DeterministicAeadKeyTemplates::Aes256Siv();
  key_templates_["AES128_CTR_HMAC_SHA256_4KB"] =
      crypto::tink::StreamingAeadKeyTemplates::Aes128CtrHmacSha256Segment4KB();
  key_templates_["AES128_CTR_HMAC_SHA256_1MB"] =
      crypto::tink::StreamingAeadKeyTemplates::Aes128CtrHmacSha256Segment1MB();
  key_templates_["AES256_CTR_HMAC_SHA256_4KB"] =
      crypto::tink::StreamingAeadKeyTemplates::Aes256CtrHmacSha256Segment4KB();
  key_templates_["AES256_CTR_HMAC_SHA256_1MB"] =
      crypto::tink::StreamingAeadKeyTemplates::Aes256CtrHmacSha256Segment1MB();
  key_templates_["AES128_GCM_HKDF_4KB"] =
      crypto::tink::StreamingAeadKeyTemplates::Aes128GcmHkdf4KB();
  key_templates_["AES256_GCM_HKDF_4KB"] =
      crypto::tink::StreamingAeadKeyTemplates::Aes256GcmHkdf4KB();
  key_templates_["AES256_GCM_HKDF_1MB"] =
      crypto::tink::StreamingAeadKeyTemplates::Aes256GcmHkdf1MB();
  key_templates_["ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM"] =
      crypto::tink::HybridKeyTemplates::EciesP256HkdfHmacSha256Aes128Gcm();
  key_templates_["ECIES_P256_COMPRESSED_HKDF_HMAC_SHA256_AES128_GCM"] = crypto::
      tink::HybridKeyTemplates::EciesP256CompressedHkdfHmacSha256Aes128Gcm();
  key_templates_["ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256"] =
      crypto::tink::HybridKeyTemplates::
          EciesP256HkdfHmacSha256Aes128CtrHmacSha256();
  key_templates_
      ["ECIES_P256_COMPRESSED_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256"] =
          crypto::tink::HybridKeyTemplates::
              EciesP256CompressedHkdfHmacSha256Aes128CtrHmacSha256();
  key_templates_["DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_128_GCM"] =
      crypto::tink::HybridKeyTemplates::HpkeX25519HkdfSha256Aes128Gcm();
  key_templates_["DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_128_GCM_RAW"] =
      crypto::tink::HybridKeyTemplates::HpkeX25519HkdfSha256Aes128GcmRaw();
  key_templates_["DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_256_GCM"] =
      crypto::tink::HybridKeyTemplates::HpkeX25519HkdfSha256Aes256Gcm();
  key_templates_["DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_256_GCM_RAW"] =
      crypto::tink::HybridKeyTemplates::HpkeX25519HkdfSha256Aes256GcmRaw();
  key_templates_["DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_CHACHA20_POLY1305"] =
      crypto::tink::HybridKeyTemplates::HpkeX25519HkdfSha256ChaCha20Poly1305();
  key_templates_["DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_CHACHA20_POLY1305_RAW"] =
      crypto::tink::HybridKeyTemplates::
          HpkeX25519HkdfSha256ChaCha20Poly1305Raw();
  key_templates_["AES_CMAC"] = crypto::tink::MacKeyTemplates::AesCmac();
  key_templates_["HMAC_SHA256_128BITTAG"] =
      crypto::tink::MacKeyTemplates::HmacSha256HalfSizeTag();
  key_templates_["HMAC_SHA256_256BITTAG"] =
      crypto::tink::MacKeyTemplates::HmacSha256();
  key_templates_["HMAC_SHA512_256BITTAG"] =
      crypto::tink::MacKeyTemplates::HmacSha512HalfSizeTag();
  key_templates_["HMAC_SHA512_512BITTAG"] =
      crypto::tink::MacKeyTemplates::HmacSha512();
  key_templates_["ECDSA_P256"] =
      crypto::tink::SignatureKeyTemplates::EcdsaP256();
  key_templates_["ECDSA_P256_RAW"] =
      crypto::tink::SignatureKeyTemplates::EcdsaP256Raw();
  key_templates_["ECDSA_P384"] =
      crypto::tink::SignatureKeyTemplates::EcdsaP384();
  key_templates_["ECDSA_P384_SHA384"] =
      crypto::tink::SignatureKeyTemplates::EcdsaP384Sha384();
  key_templates_["ECDSA_P384_SHA512"] =
      crypto::tink::SignatureKeyTemplates::EcdsaP384Sha512();
  key_templates_["ECDSA_P521"] =
      crypto::tink::SignatureKeyTemplates::EcdsaP521();
  key_templates_["ECDSA_P256_IEEE_P1363"] =
      crypto::tink::SignatureKeyTemplates::EcdsaP256Ieee();
  key_templates_["ECDSA_P384_IEEE_P1363"] =
      crypto::tink::SignatureKeyTemplates::EcdsaP384Ieee();
  key_templates_["ECDSA_P521_IEEE_P1363"] =
      crypto::tink::SignatureKeyTemplates::EcdsaP521Ieee();
  key_templates_["ED25519"] = crypto::tink::SignatureKeyTemplates::Ed25519();
  key_templates_["RSA_SSA_PKCS1_3072_SHA256_F4"] =
      crypto::tink::SignatureKeyTemplates::RsaSsaPkcs13072Sha256F4();
  key_templates_["RSA_SSA_PKCS1_4096_SHA512_F4"] =
      crypto::tink::SignatureKeyTemplates::RsaSsaPkcs14096Sha512F4();
  key_templates_["RSA_SSA_PSS_3072_SHA256_SHA256_32_F4"] =
      crypto::tink::SignatureKeyTemplates::RsaSsaPss3072Sha256Sha256F4();
  key_templates_["RSA_SSA_PSS_4096_SHA512_SHA512_64_F4"] =
      crypto::tink::SignatureKeyTemplates::RsaSsaPss4096Sha512Sha512F4();
  key_templates_["AES_CMAC_PRF"] = crypto::tink::PrfKeyTemplates::AesCmac();
  key_templates_["HMAC_SHA256_PRF"] =
      crypto::tink::PrfKeyTemplates::HmacSha256();
  key_templates_["HMAC_SHA512_PRF"] =
      crypto::tink::PrfKeyTemplates::HmacSha512();
  key_templates_["HKDF_SHA256"] = crypto::tink::PrfKeyTemplates::HkdfSha256();
  key_templates_["JWT_HS256"] = crypto::tink::JwtHs256Template();
  key_templates_["JWT_HS256_RAW"] = crypto::tink::RawJwtHs256Template();
  key_templates_["JWT_HS384"] = crypto::tink::JwtHs384Template();
  key_templates_["JWT_HS384_RAW"] = crypto::tink::RawJwtHs384Template();
  key_templates_["JWT_HS512"] = crypto::tink::JwtHs512Template();
  key_templates_["JWT_HS512_RAW"] = crypto::tink::RawJwtHs512Template();
  key_templates_["JWT_ES256"] = crypto::tink::JwtEs256Template();
  key_templates_["JWT_ES256_RAW"] = crypto::tink::RawJwtEs256Template();
  key_templates_["JWT_ES384"] = crypto::tink::JwtEs384Template();
  key_templates_["JWT_ES384_RAW"] = crypto::tink::RawJwtEs384Template();
  key_templates_["JWT_ES512"] = crypto::tink::JwtEs512Template();
  key_templates_["JWT_ES512_RAW"] = crypto::tink::RawJwtEs512Template();
  key_templates_["JWT_RS256_2048_F4"] =
      crypto::tink::JwtRs256_2048_F4_Template();
  key_templates_["JWT_RS256_2048_F4_RAW"] =
      crypto::tink::RawJwtRs256_2048_F4_Template();
  key_templates_["JWT_RS256_3072_F4"] =
      crypto::tink::JwtRs256_3072_F4_Template();
  key_templates_["JWT_RS256_3072_F4_RAW"] =
      crypto::tink::RawJwtRs256_3072_F4_Template();
  key_templates_["JWT_RS384_3072_F4"] =
      crypto::tink::JwtRs384_3072_F4_Template();
  key_templates_["JWT_RS384_3072_F4_RAW"] =
      crypto::tink::RawJwtRs384_3072_F4_Template();
  key_templates_["JWT_RS512_4096_F4"] =
      crypto::tink::JwtRs512_4096_F4_Template();
  key_templates_["JWT_RS512_4096_F4_RAW"] =
      crypto::tink::RawJwtRs512_4096_F4_Template();
  key_templates_["JWT_PS256_2048_F4"] =
      crypto::tink::JwtPs256_2048_F4_Template();
  key_templates_["JWT_PS256_2048_F4_RAW"] =
      crypto::tink::RawJwtPs256_2048_F4_Template();
  key_templates_["JWT_PS256_3072_F4"] =
      crypto::tink::JwtPs256_3072_F4_Template();
  key_templates_["JWT_PS256_3072_F4_RAW"] =
      crypto::tink::RawJwtPs256_3072_F4_Template();
  key_templates_["JWT_PS384_3072_F4"] =
      crypto::tink::JwtPs384_3072_F4_Template();
  key_templates_["JWT_PS384_3072_F4_RAW"] =
      crypto::tink::RawJwtPs384_3072_F4_Template();
  key_templates_["JWT_PS512_4096_F4"] =
      crypto::tink::JwtPs512_4096_F4_Template();
  key_templates_["JWT_PS512_4096_F4_RAW"] =
      crypto::tink::RawJwtPs512_4096_F4_Template();
}

// Returns the key template for the given template name.
grpc::Status KeysetImpl::GetTemplate(grpc::ServerContext* context,
                                       const KeysetTemplateRequest* request,
                                       KeysetTemplateResponse* response) {
  auto it = key_templates_.find(request->template_name());
  if (it == key_templates_.end()) {
    response->set_err(
        absl::StrCat("key template not found: ", request->template_name()));
    return grpc::Status::OK;
  }
  std::string templ;
  if (!it->second.SerializeToString(&templ)) {
    response->set_err("Failed to serialize template.");
    return grpc::Status::OK;
  }
  response->set_key_template(templ);
  return grpc::Status::OK;
}

// Generates a new keyset with one key from a template.
grpc::Status KeysetImpl::Generate(grpc::ServerContext* context,
                                    const KeysetGenerateRequest* request,
                                    KeysetGenerateResponse* response) {
  KeyTemplate key_template;
  if (!key_template.ParseFromString(request->template_())) {
    response->set_err("Could not parse the key template");
    return grpc::Status::OK;
  }
  auto handle_result = ::crypto::tink::KeysetHandle::GenerateNew(key_template);
  if (!handle_result.ok()) {
    response->set_err(std::string(handle_result.status().message()));
    return grpc::Status::OK;
  }
  std::stringbuf keyset;
  auto writer_result =
      BinaryKeysetWriter::New(absl::make_unique<std::ostream>(&keyset));
  if (!writer_result.ok()) {
    response->set_err(std::string(writer_result.status().message()));
    return grpc::Status::OK;
  }
  auto status = CleartextKeysetHandle::Write(writer_result.value().get(),
                                             *handle_result.value());
  if (!status.ok()) {
    response->set_err(std::string(status.message()));
    return grpc::Status::OK;
  }
  response->set_keyset(keyset.str());
  return grpc::Status::OK;
}

// Returns a public keyset for a given private keyset.
grpc::Status KeysetImpl::Public(grpc::ServerContext* context,
                                  const KeysetPublicRequest* request,
                                  KeysetPublicResponse* response) {
  auto reader_result = BinaryKeysetReader::New(request->private_keyset());
  if (!reader_result.ok()) {
    response->set_err(std::string(reader_result.status().message()));
    return grpc::Status::OK;
  }
  auto private_handle_result =
      CleartextKeysetHandle::Read(std::move(reader_result.value()));
  if (!private_handle_result.ok()) {
    response->set_err(std::string(private_handle_result.status().message()));
    return grpc::Status::OK;
  }
  auto public_handle_result =
      private_handle_result.value()->GetPublicKeysetHandle();
  if (!public_handle_result.ok()) {
    response->set_err(std::string(public_handle_result.status().message()));
    return grpc::Status::OK;
  }
  std::stringbuf public_keyset;
  auto writer_result =
      BinaryKeysetWriter::New(absl::make_unique<std::ostream>(&public_keyset));
  if (!writer_result.ok()) {
    response->set_err(std::string(writer_result.status().message()));
    return grpc::Status::OK;
  }
  auto status = CleartextKeysetHandle::Write(writer_result.value().get(),
                                             *public_handle_result.value());
  if (!status.ok()) {
    response->set_err(std::string(status.message()));
    return grpc::Status::OK;
  }
  response->set_public_keyset(public_keyset.str());
  return grpc::Status::OK;
}

// Converts a keyset from binary to JSON format.
grpc::Status KeysetImpl::ToJson(grpc::ServerContext* context,
                                  const KeysetToJsonRequest* request,
                                  KeysetToJsonResponse* response) {
  auto reader_result = BinaryKeysetReader::New(request->keyset());
  if (!reader_result.ok()) {
    response->set_err(std::string(reader_result.status().message()));
    return grpc::Status::OK;
  }
  auto handle_result =
      CleartextKeysetHandle::Read(std::move(reader_result.value()));
  if (!handle_result.ok()) {
    response->set_err(std::string(handle_result.status().message()));
    return grpc::Status::OK;
  }
  std::stringbuf json_keyset;
  auto writer_result =
      JsonKeysetWriter::New(absl::make_unique<std::ostream>(&json_keyset));
  if (!writer_result.ok()) {
    response->set_err(std::string(writer_result.status().message()));
    return grpc::Status::OK;
  }
  auto status = CleartextKeysetHandle::Write(writer_result.value().get(),
                                             *handle_result.value());
  if (!status.ok()) {
    response->set_err(std::string(status.message()));
    return grpc::Status::OK;
  }
  response->set_json_keyset(json_keyset.str());
  return grpc::Status::OK;
}

// Converts a keyset from JSON to binary format.
grpc::Status KeysetImpl::FromJson(grpc::ServerContext* context,
                                    const KeysetFromJsonRequest* request,
                                    KeysetFromJsonResponse* response) {
  auto reader_result = JsonKeysetReader::New(request->json_keyset());
  if (!reader_result.ok()) {
    response->set_err(std::string(reader_result.status().message()));
    return grpc::Status::OK;
  }
  auto handle_result =
      CleartextKeysetHandle::Read(std::move(reader_result.value()));
  if (!handle_result.ok()) {
    response->set_err(std::string(handle_result.status().message()));
    return grpc::Status::OK;
  }
  std::stringbuf keyset;
  auto writer_result =
      BinaryKeysetWriter::New(absl::make_unique<std::ostream>(&keyset));
  if (!writer_result.ok()) {
    response->set_err(std::string(writer_result.status().message()));
    return grpc::Status::OK;
  }
  auto status = CleartextKeysetHandle::Write(writer_result.value().get(),
                                             *handle_result.value());
  if (!status.ok()) {
    response->set_err(std::string(status.message()));
    return grpc::Status::OK;
  }
  response->set_keyset(keyset.str());
  return grpc::Status::OK;
}

grpc::Status KeysetImpl::WriteEncrypted(
    grpc::ServerContext* context, const KeysetWriteEncryptedRequest* request,
    KeysetWriteEncryptedResponse* response) {
  StatusOr<std::unique_ptr<KeysetReader>>
      master_keyset_reader = BinaryKeysetReader::New(request->master_keyset());
  if (!master_keyset_reader.ok()) {
    response->set_err(std::string(master_keyset_reader.status().message()));
    return grpc::Status::OK;
  }
  StatusOr<std::unique_ptr<KeysetHandle>> master_keyset_handle =
      CleartextKeysetHandle::Read(*std::move(master_keyset_reader));
  if (!master_keyset_handle.ok()) {
    response->set_err(std::string(master_keyset_handle.status().message()));
    return grpc::Status::OK;
  }
  StatusOr<std::unique_ptr<crypto::tink::Aead>> master_aead =
      (*master_keyset_handle)->GetPrimitive<crypto::tink::Aead>();
  if (!master_aead.ok()) {
    response->set_err(std::string(master_aead.status().message()));
    return grpc::Status::OK;
  }

  StatusOr<std::unique_ptr<KeysetReader>> keyset_reader =
      BinaryKeysetReader::New(request->keyset());
  if (!keyset_reader.ok()) {
    response->set_err(std::string(keyset_reader.status().message()));
    return grpc::Status::OK;
  }
  StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle =
      CleartextKeysetHandle::Read(*std::move(keyset_reader));
  if (!keyset_handle.ok()) {
    response->set_err(std::string(keyset_handle.status().message()));
    return grpc::Status::OK;
  }

  std::stringbuf encrypted_keyset;
  std::unique_ptr<KeysetWriter> keyset_writer;

  if (request->keyset_writer_type() == KEYSET_WRITER_BINARY) {
    StatusOr<std::unique_ptr<BinaryKeysetWriter>> binary_keyset_writer =
        BinaryKeysetWriter::New(
            absl::make_unique<std::ostream>(&encrypted_keyset));
    if (!binary_keyset_writer.ok()) {
      response->set_err(std::string(binary_keyset_writer.status().message()));
      return grpc::Status::OK;
    }
    keyset_writer = *std::move(binary_keyset_writer);
  } else if (request->keyset_writer_type() == KEYSET_WRITER_JSON) {
    StatusOr<std::unique_ptr<JsonKeysetWriter>> json_keyset_writer =
        JsonKeysetWriter::New(
            absl::make_unique<std::ostream>(&encrypted_keyset));
    if (!json_keyset_writer.ok()) {
      response->set_err(std::string(json_keyset_writer.status().message()));
      return grpc::Status::OK;
    }
    keyset_writer = *std::move(json_keyset_writer);
  } else {
    return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
                        "unknown keyset_writer_type");
  }

  if (request->has_associated_data()) {
    crypto::tink::util::Status status =
        (*keyset_handle)
            ->WriteWithAssociatedData(keyset_writer.get(), **master_aead,
                                      request->associated_data().value());
    if (!status.ok()) {
      response->set_err(std::string(status.message()));
      return grpc::Status::OK;
    }
  } else {
    crypto::tink::util::Status status =
        (*keyset_handle)->Write(keyset_writer.get(), **master_aead);
    if (!status.ok()) {
      response->set_err(std::string(status.message()));
      return grpc::Status::OK;
    }
  }
  response->set_encrypted_keyset(encrypted_keyset.str());
  return grpc::Status::OK;
}

grpc::Status KeysetImpl::ReadEncrypted(
    grpc::ServerContext* context, const KeysetReadEncryptedRequest* request,
    KeysetReadEncryptedResponse* response) {
  StatusOr<std::unique_ptr<KeysetReader>> master_keyset_reader =
      BinaryKeysetReader::New(request->master_keyset());
  if (!master_keyset_reader.ok()) {
    response->set_err(std::string(master_keyset_reader.status().message()));
    return grpc::Status::OK;
  }
  StatusOr<std::unique_ptr<KeysetHandle>> master_keyset_handle =
      CleartextKeysetHandle::Read(*std::move(master_keyset_reader));
  if (!master_keyset_handle.ok()) {
    response->set_err(std::string(master_keyset_handle.status().message()));
    return grpc::Status::OK;
  }
  StatusOr<std::unique_ptr<crypto::tink::Aead>> master_aead =
      (*master_keyset_handle)->GetPrimitive<crypto::tink::Aead>();
  if (!master_aead.ok()) {
    response->set_err(std::string(master_aead.status().message()));
    return grpc::Status::OK;
  }

  std::unique_ptr<KeysetReader> keyset_reader;
  if (request->keyset_reader_type() == KEYSET_READER_BINARY) {
    StatusOr<std::unique_ptr<KeysetReader>> binary_keyset_reader =
        BinaryKeysetReader::New(request->encrypted_keyset());
    if (!binary_keyset_reader.ok()) {
      response->set_err(std::string(binary_keyset_reader.status().message()));
      return grpc::Status::OK;
    }
    keyset_reader = *std::move(binary_keyset_reader);
  } else if (request->keyset_reader_type() == KEYSET_READER_JSON) {
    StatusOr<std::unique_ptr<KeysetReader>> json_keyset_reader =
        JsonKeysetReader::New(request->encrypted_keyset());
    if (!json_keyset_reader.ok()) {
      response->set_err(std::string(json_keyset_reader.status().message()));
      return grpc::Status::OK;
    }
    keyset_reader = *std::move(json_keyset_reader);
  } else {
    return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
                        "unknown keyset_writer_type");
  }

  std::unique_ptr<KeysetHandle> keyset_handle;
  if (request->has_associated_data()) {
    StatusOr<std::unique_ptr<KeysetHandle>> read_result =
        ::crypto::tink::KeysetHandle::ReadWithAssociatedData(
            std::move(keyset_reader), **master_aead,
            request->associated_data().value());
    if (!read_result.ok()) {
      response->set_err(std::string(read_result.status().message()));
      return grpc::Status::OK;
    }
    keyset_handle = *std::move(read_result);
  } else {
    StatusOr<std::unique_ptr<KeysetHandle>> read_result =
        ::crypto::tink::KeysetHandle::Read(std::move(keyset_reader),
                                           **master_aead);
    if (!read_result.ok()) {
      response->set_err(std::string(read_result.status().message()));
      return grpc::Status::OK;
    }
    keyset_handle = *std::move(read_result);
  }

  std::stringbuf keyset;
  StatusOr<std::unique_ptr<BinaryKeysetWriter>> keyset_writer =
      BinaryKeysetWriter::New(absl::make_unique<std::ostream>(&keyset));
  if (!keyset_writer.ok()) {
    response->set_err(std::string(keyset_writer.status().message()));
    return grpc::Status::OK;
  }
  crypto::tink::util::Status status =
      CleartextKeysetHandle::Write(keyset_writer->get(), *keyset_handle);
  if (!status.ok()) {
    response->set_err(std::string(status.message()));
    return grpc::Status::OK;
  }
  response->set_keyset(keyset.str());
  return grpc::Status::OK;
}

}  // namespace tink_testing_api
