1 // Copyright 2018 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ///////////////////////////////////////////////////////////////////////////////
16
17 #include "tink/json_keyset_writer.h"
18
19 #include <istream>
20 #include <memory>
21 #include <ostream>
22 #include <sstream>
23 #include <string>
24 #include <utility>
25
26 #include "absl/status/status.h"
27 #include "absl/strings/escaping.h"
28 #include "include/rapidjson/document.h"
29 #include "include/rapidjson/prettywriter.h"
30 #include "tink/util/enums.h"
31 #include "tink/util/errors.h"
32 #include "tink/util/protobuf_helper.h"
33 #include "tink/util/status.h"
34 #include "tink/util/statusor.h"
35 #include "proto/tink.pb.h"
36
37 namespace crypto {
38 namespace tink {
39
40
41 using google::crypto::tink::EncryptedKeyset;
42 using google::crypto::tink::KeyData;
43 using google::crypto::tink::Keyset;
44 using google::crypto::tink::KeysetInfo;
45 using util::Enums;
46
47 namespace {
48
49 // Helpers for transoforming Keyset-protos to JSON strings.
ToJson(const KeyData & key_data,rapidjson::Value * json_key_data,rapidjson::Document::AllocatorType * allocator)50 util::Status ToJson(const KeyData& key_data,
51 rapidjson::Value* json_key_data,
52 rapidjson::Document::AllocatorType* allocator) {
53 rapidjson::Value type_url(rapidjson::kStringType);
54 type_url.SetString(key_data.type_url().c_str(), *allocator);
55 json_key_data->AddMember("typeUrl", type_url, *allocator);
56
57 rapidjson::Value material_type(rapidjson::kStringType);
58 material_type.SetString(Enums::KeyMaterialName(key_data.key_material_type()),
59 *allocator);
60 json_key_data->AddMember("keyMaterialType", material_type, *allocator);
61
62 std::string base64_string;
63 absl::Base64Escape(key_data.value(), &base64_string);
64 rapidjson::Value key_value(rapidjson::kStringType);
65 key_value.SetString(base64_string.c_str(), *allocator);
66 json_key_data->AddMember("value", key_value, *allocator);
67
68 return util::OkStatus();
69 }
70
ToJson(const Keyset::Key & key,rapidjson::Value * json_key,rapidjson::Document::AllocatorType * allocator)71 util::Status ToJson(const Keyset::Key& key,
72 rapidjson::Value* json_key,
73 rapidjson::Document::AllocatorType* allocator) {
74 rapidjson::Value key_id(rapidjson::kNumberType);
75 key_id.SetUint(key.key_id());
76 json_key->AddMember("keyId", key_id, *allocator);
77
78 rapidjson::Value key_status(rapidjson::kStringType);
79 key_status.SetString(Enums::KeyStatusName(key.status()), *allocator);
80 json_key->AddMember("status", key_status, *allocator);
81
82 rapidjson::Value prefix_type(rapidjson::kStringType);
83 prefix_type.SetString(Enums::OutputPrefixName(key.output_prefix_type()),
84 *allocator);
85 json_key->AddMember("outputPrefixType", prefix_type, *allocator);
86
87 rapidjson::Value json_key_data(rapidjson::kObjectType);
88 auto status = ToJson(key.key_data(), &json_key_data, allocator);
89 if (!status.ok()) return status;
90 json_key->AddMember("keyData", json_key_data, *allocator);
91 return util::OkStatus();
92 }
93
ToJsonString(const Keyset & keyset)94 util::StatusOr<std::string> ToJsonString(const Keyset& keyset) {
95 rapidjson::Document json_doc(rapidjson::kObjectType);
96 auto& allocator = json_doc.GetAllocator();
97
98 rapidjson::Value primary_key_id(rapidjson::kNumberType);
99 primary_key_id.SetUint(keyset.primary_key_id());
100 json_doc.AddMember("primaryKeyId", primary_key_id, allocator);
101
102 rapidjson::Value key_array(rapidjson::kArrayType);
103 for (const Keyset::Key& key : keyset.key()) {
104 rapidjson::Value json_key(rapidjson::kObjectType);
105 auto status = ToJson(key, &json_key, &allocator);
106 if (!status.ok()) return status;
107 key_array.PushBack(json_key, allocator);
108 }
109 json_doc.AddMember("key", key_array, allocator);
110 rapidjson::StringBuffer string_buffer;
111 rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(string_buffer);
112 json_doc.Accept(writer);
113 return std::string(string_buffer.GetString());
114 }
115
ToJson(const KeysetInfo::KeyInfo & key_info,rapidjson::Value * json_key_info,rapidjson::Document::AllocatorType * allocator)116 util::Status ToJson(const KeysetInfo::KeyInfo& key_info,
117 rapidjson::Value* json_key_info,
118 rapidjson::Document::AllocatorType* allocator) {
119 rapidjson::Value type_url(rapidjson::kStringType);
120 type_url.SetString(key_info.type_url().c_str(), *allocator);
121 json_key_info->AddMember("typeUrl", type_url, *allocator);
122
123 rapidjson::Value key_id(rapidjson::kNumberType);
124 key_id.SetUint(key_info.key_id());
125 json_key_info->AddMember("keyId", key_id, *allocator);
126
127 rapidjson::Value key_status(rapidjson::kStringType);
128 key_status.SetString(Enums::KeyStatusName(key_info.status()), *allocator);
129 json_key_info->AddMember("status", key_status, *allocator);
130
131 rapidjson::Value prefix_type(rapidjson::kStringType);
132 prefix_type.SetString(Enums::OutputPrefixName(key_info.output_prefix_type()),
133 *allocator);
134 json_key_info->AddMember("outputPrefixType", prefix_type, *allocator);
135 return util::OkStatus();
136 }
137
ToJson(const KeysetInfo & keyset_info,rapidjson::Value * json_keyset_info,rapidjson::Document::AllocatorType * allocator)138 util::Status ToJson(const KeysetInfo& keyset_info,
139 rapidjson::Value* json_keyset_info,
140 rapidjson::Document::AllocatorType* allocator) {
141 rapidjson::Value primary_key_id(rapidjson::kNumberType);
142 primary_key_id.SetUint(keyset_info.primary_key_id());
143 json_keyset_info->AddMember("primaryKeyId", primary_key_id, *allocator);
144
145 rapidjson::Value key_info_array(rapidjson::kArrayType);
146 for (const KeysetInfo::KeyInfo& key_info : keyset_info.key_info()) {
147 rapidjson::Value json_key_info(rapidjson::kObjectType);
148 auto status = ToJson(key_info, &json_key_info, allocator);
149 if (!status.ok()) return status;
150 key_info_array.PushBack(json_key_info, *allocator);
151 }
152 json_keyset_info->AddMember("keyInfo", key_info_array, *allocator);
153 return util::OkStatus();
154 }
155
ToJsonString(const EncryptedKeyset & keyset)156 util::StatusOr<std::string> ToJsonString(const EncryptedKeyset& keyset) {
157 rapidjson::Document json_doc(rapidjson::kObjectType);
158 auto& allocator = json_doc.GetAllocator();
159
160 std::string base64_string;
161 absl::Base64Escape(keyset.encrypted_keyset(), &base64_string);
162 rapidjson::Value encrypted_keyset(rapidjson::kStringType);
163 encrypted_keyset.SetString(base64_string.c_str(), allocator);
164 json_doc.AddMember("encryptedKeyset", encrypted_keyset, allocator);
165
166 if (keyset.has_keyset_info()) {
167 rapidjson::Value json_keyset_info(rapidjson::kObjectType);
168 auto status = ToJson(keyset.keyset_info(), &json_keyset_info, &allocator);
169 if (!status.ok()) return status;
170 json_doc.AddMember("keysetInfo", json_keyset_info, allocator);
171 }
172
173 rapidjson::StringBuffer string_buffer;
174 rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(string_buffer);
175 json_doc.Accept(writer);
176 return std::string(string_buffer.GetString());
177 }
178
WriteData(absl::string_view data,std::ostream * destination)179 util::Status WriteData(absl::string_view data, std::ostream* destination) {
180 (*destination) << data;
181 if (destination->fail()) {
182 return util::Status(absl::StatusCode::kUnknown,
183 "Error writing to the destination stream.");
184 }
185 return util::OkStatus();
186 }
187
188 } // anonymous namespace
189
190
191 // static
New(std::unique_ptr<std::ostream> destination_stream)192 util::StatusOr<std::unique_ptr<JsonKeysetWriter>> JsonKeysetWriter::New(
193 std::unique_ptr<std::ostream> destination_stream) {
194 if (destination_stream == nullptr) {
195 return util::Status(absl::StatusCode::kInvalidArgument,
196 "destination_stream must be non-null.");
197 }
198 std::unique_ptr<JsonKeysetWriter> writer(
199 new JsonKeysetWriter(std::move(destination_stream)));
200 return std::move(writer);
201 }
202
Write(const Keyset & keyset)203 util::Status JsonKeysetWriter::Write(const Keyset& keyset) {
204 auto json_string_result = ToJsonString(keyset);
205 if (!json_string_result.ok()) return json_string_result.status();
206 return WriteData(json_string_result.value(), destination_stream_.get());
207 }
208
Write(const EncryptedKeyset & encrypted_keyset)209 util::Status JsonKeysetWriter::Write(
210 const EncryptedKeyset& encrypted_keyset) {
211 auto json_string_result = ToJsonString(encrypted_keyset);
212 if (!json_string_result.ok()) return json_string_result.status();
213 return WriteData(json_string_result.value(), destination_stream_.get());
214 }
215
216 } // namespace tink
217 } // namespace crypto
218