blob: 7c3f19c529fae59eb1e75a783b7646624f73acfd [file] [log] [blame]
// Copyright 2018 Google Inc.
//
// 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.
//
///////////////////////////////////////////////////////////////////////////////
#include "tink/json_keyset_writer.h"
#include <istream>
#include <memory>
#include <ostream>
#include <sstream>
#include <string>
#include <utility>
#include "absl/status/status.h"
#include "absl/strings/escaping.h"
#include "include/rapidjson/document.h"
#include "include/rapidjson/prettywriter.h"
#include "tink/util/enums.h"
#include "tink/util/errors.h"
#include "tink/util/protobuf_helper.h"
#include "tink/util/status.h"
#include "tink/util/statusor.h"
#include "proto/tink.pb.h"
namespace crypto {
namespace tink {
using google::crypto::tink::EncryptedKeyset;
using google::crypto::tink::KeyData;
using google::crypto::tink::Keyset;
using google::crypto::tink::KeysetInfo;
using util::Enums;
namespace {
// Helpers for transoforming Keyset-protos to JSON strings.
util::Status ToJson(const KeyData& key_data,
rapidjson::Value* json_key_data,
rapidjson::Document::AllocatorType* allocator) {
rapidjson::Value type_url(rapidjson::kStringType);
type_url.SetString(key_data.type_url().c_str(), *allocator);
json_key_data->AddMember("typeUrl", type_url, *allocator);
rapidjson::Value material_type(rapidjson::kStringType);
material_type.SetString(Enums::KeyMaterialName(key_data.key_material_type()),
*allocator);
json_key_data->AddMember("keyMaterialType", material_type, *allocator);
std::string base64_string;
absl::Base64Escape(key_data.value(), &base64_string);
rapidjson::Value key_value(rapidjson::kStringType);
key_value.SetString(base64_string.c_str(), *allocator);
json_key_data->AddMember("value", key_value, *allocator);
return util::OkStatus();
}
util::Status ToJson(const Keyset::Key& key,
rapidjson::Value* json_key,
rapidjson::Document::AllocatorType* allocator) {
rapidjson::Value key_id(rapidjson::kNumberType);
key_id.SetUint(key.key_id());
json_key->AddMember("keyId", key_id, *allocator);
rapidjson::Value key_status(rapidjson::kStringType);
key_status.SetString(Enums::KeyStatusName(key.status()), *allocator);
json_key->AddMember("status", key_status, *allocator);
rapidjson::Value prefix_type(rapidjson::kStringType);
prefix_type.SetString(Enums::OutputPrefixName(key.output_prefix_type()),
*allocator);
json_key->AddMember("outputPrefixType", prefix_type, *allocator);
rapidjson::Value json_key_data(rapidjson::kObjectType);
auto status = ToJson(key.key_data(), &json_key_data, allocator);
if (!status.ok()) return status;
json_key->AddMember("keyData", json_key_data, *allocator);
return util::OkStatus();
}
util::StatusOr<std::string> ToJsonString(const Keyset& keyset) {
rapidjson::Document json_doc(rapidjson::kObjectType);
auto& allocator = json_doc.GetAllocator();
rapidjson::Value primary_key_id(rapidjson::kNumberType);
primary_key_id.SetUint(keyset.primary_key_id());
json_doc.AddMember("primaryKeyId", primary_key_id, allocator);
rapidjson::Value key_array(rapidjson::kArrayType);
for (const Keyset::Key& key : keyset.key()) {
rapidjson::Value json_key(rapidjson::kObjectType);
auto status = ToJson(key, &json_key, &allocator);
if (!status.ok()) return status;
key_array.PushBack(json_key, allocator);
}
json_doc.AddMember("key", key_array, allocator);
rapidjson::StringBuffer string_buffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(string_buffer);
json_doc.Accept(writer);
return std::string(string_buffer.GetString());
}
util::Status ToJson(const KeysetInfo::KeyInfo& key_info,
rapidjson::Value* json_key_info,
rapidjson::Document::AllocatorType* allocator) {
rapidjson::Value type_url(rapidjson::kStringType);
type_url.SetString(key_info.type_url().c_str(), *allocator);
json_key_info->AddMember("typeUrl", type_url, *allocator);
rapidjson::Value key_id(rapidjson::kNumberType);
key_id.SetUint(key_info.key_id());
json_key_info->AddMember("keyId", key_id, *allocator);
rapidjson::Value key_status(rapidjson::kStringType);
key_status.SetString(Enums::KeyStatusName(key_info.status()), *allocator);
json_key_info->AddMember("status", key_status, *allocator);
rapidjson::Value prefix_type(rapidjson::kStringType);
prefix_type.SetString(Enums::OutputPrefixName(key_info.output_prefix_type()),
*allocator);
json_key_info->AddMember("outputPrefixType", prefix_type, *allocator);
return util::OkStatus();
}
util::Status ToJson(const KeysetInfo& keyset_info,
rapidjson::Value* json_keyset_info,
rapidjson::Document::AllocatorType* allocator) {
rapidjson::Value primary_key_id(rapidjson::kNumberType);
primary_key_id.SetUint(keyset_info.primary_key_id());
json_keyset_info->AddMember("primaryKeyId", primary_key_id, *allocator);
rapidjson::Value key_info_array(rapidjson::kArrayType);
for (const KeysetInfo::KeyInfo& key_info : keyset_info.key_info()) {
rapidjson::Value json_key_info(rapidjson::kObjectType);
auto status = ToJson(key_info, &json_key_info, allocator);
if (!status.ok()) return status;
key_info_array.PushBack(json_key_info, *allocator);
}
json_keyset_info->AddMember("keyInfo", key_info_array, *allocator);
return util::OkStatus();
}
util::StatusOr<std::string> ToJsonString(const EncryptedKeyset& keyset) {
rapidjson::Document json_doc(rapidjson::kObjectType);
auto& allocator = json_doc.GetAllocator();
std::string base64_string;
absl::Base64Escape(keyset.encrypted_keyset(), &base64_string);
rapidjson::Value encrypted_keyset(rapidjson::kStringType);
encrypted_keyset.SetString(base64_string.c_str(), allocator);
json_doc.AddMember("encryptedKeyset", encrypted_keyset, allocator);
if (keyset.has_keyset_info()) {
rapidjson::Value json_keyset_info(rapidjson::kObjectType);
auto status = ToJson(keyset.keyset_info(), &json_keyset_info, &allocator);
if (!status.ok()) return status;
json_doc.AddMember("keysetInfo", json_keyset_info, allocator);
}
rapidjson::StringBuffer string_buffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(string_buffer);
json_doc.Accept(writer);
return std::string(string_buffer.GetString());
}
util::Status WriteData(absl::string_view data, std::ostream* destination) {
(*destination) << data;
if (destination->fail()) {
return util::Status(absl::StatusCode::kUnknown,
"Error writing to the destination stream.");
}
return util::OkStatus();
}
} // anonymous namespace
// static
util::StatusOr<std::unique_ptr<JsonKeysetWriter>> JsonKeysetWriter::New(
std::unique_ptr<std::ostream> destination_stream) {
if (destination_stream == nullptr) {
return util::Status(absl::StatusCode::kInvalidArgument,
"destination_stream must be non-null.");
}
std::unique_ptr<JsonKeysetWriter> writer(
new JsonKeysetWriter(std::move(destination_stream)));
return std::move(writer);
}
util::Status JsonKeysetWriter::Write(const Keyset& keyset) {
auto json_string_result = ToJsonString(keyset);
if (!json_string_result.ok()) return json_string_result.status();
return WriteData(json_string_result.value(), destination_stream_.get());
}
util::Status JsonKeysetWriter::Write(
const EncryptedKeyset& encrypted_keyset) {
auto json_string_result = ToJsonString(encrypted_keyset);
if (!json_string_result.ok()) return json_string_result.status();
return WriteData(json_string_result.value(), destination_stream_.get());
}
} // namespace tink
} // namespace crypto