Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2023 Project CHIP Authors
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : #include "lib/support/CHIPMemString.h"
19 : #include "lib/support/ScopedBuffer.h"
20 : #include <json/json.h>
21 : #include <lib/core/DataModelTypes.h>
22 : #include <lib/support/Base64.h>
23 : #include <lib/support/SafeInt.h>
24 : #include <lib/support/jsontlv/ElementTypes.h>
25 : #include <lib/support/jsontlv/TlvToJson.h>
26 :
27 : namespace chip {
28 :
29 : namespace {
30 :
31 : // actual value of this does not actually matter, however we need
32 : // a value to be able to read 32-bit implicit profile tags
33 : //
34 : // JSON format never has this and TLV payload contains "implicit profile"
35 : // and this value is never stored.
36 : constexpr uint32_t kTemporaryImplicitProfileId = 0xFF01;
37 :
38 : /// RAII to switch the implicit profile id for a reader
39 : class ImplicitProfileIdChange
40 : {
41 : public:
42 63 : ImplicitProfileIdChange(TLV::TLVReader & reader, uint32_t id) : mReader(reader), mOldImplicitProfileId(reader.ImplicitProfileId)
43 : {
44 63 : reader.ImplicitProfileId = id;
45 63 : }
46 63 : ~ImplicitProfileIdChange() { mReader.ImplicitProfileId = mOldImplicitProfileId; }
47 :
48 : private:
49 : TLV::TLVReader & mReader;
50 : uint32_t mOldImplicitProfileId;
51 : };
52 :
53 177 : const char * GetJsonElementStrFromType(const ElementTypeContext & ctx)
54 : {
55 177 : switch (ctx.tlvType)
56 : {
57 30 : case TLV::kTLVType_UnsignedInteger:
58 30 : return kElementTypeUInt;
59 25 : case TLV::kTLVType_SignedInteger:
60 25 : return kElementTypeInt;
61 17 : case TLV::kTLVType_Boolean:
62 17 : return kElementTypeBool;
63 32 : case TLV::kTLVType_FloatingPointNumber:
64 32 : return ctx.isDouble ? kElementTypeDouble : kElementTypeFloat;
65 12 : case TLV::kTLVType_ByteString:
66 12 : return kElementTypeBytes;
67 13 : case TLV::kTLVType_UTF8String:
68 13 : return kElementTypeString;
69 5 : case TLV::kTLVType_Null:
70 5 : return kElementTypeNull;
71 13 : case TLV::kTLVType_Structure:
72 13 : return kElementTypeStruct;
73 25 : case TLV::kTLVType_Array:
74 25 : return kElementTypeArray;
75 5 : default:
76 5 : return kElementTypeEmpty;
77 : }
78 : };
79 :
80 : /*
81 : * Encapsulates the element information required to construct a JSON element name string in a JSON object.
82 : *
83 : * The generated JSON element name string is constructed as:
84 : * 'TagNumber:ElementType-SubElementType'.
85 : */
86 : struct JsonObjectElementContext
87 : {
88 223 : JsonObjectElementContext(TLV::TLVReader & reader)
89 223 : {
90 223 : tag = reader.GetTag();
91 223 : implicitProfileId = reader.ImplicitProfileId;
92 223 : type.tlvType = reader.GetType();
93 223 : if (type.tlvType == TLV::kTLVType_FloatingPointNumber)
94 : {
95 41 : type.isDouble = reader.IsElementDouble();
96 : }
97 223 : }
98 :
99 152 : std::string GenerateJsonElementName() const
100 : {
101 152 : std::string str = "???";
102 152 : if (TLV::IsContextTag(tag))
103 : {
104 : // common case for context tags: raw value
105 140 : str = std::to_string(TLV::TagNumFromTag(tag));
106 : }
107 12 : else if (TLV::IsProfileTag(tag))
108 : {
109 12 : if (TLV::ProfileIdFromTag(tag) == implicitProfileId)
110 : {
111 5 : str = std::to_string(TLV::TagNumFromTag(tag));
112 : }
113 : else
114 : {
115 7 : uint32_t tagNumber = (static_cast<uint32_t>(TLV::VendorIdFromTag(tag)) << 16) | TLV::TagNumFromTag(tag);
116 7 : str = std::to_string(tagNumber);
117 : }
118 : }
119 152 : str = str + ":" + GetJsonElementStrFromType(type);
120 152 : if (type.tlvType == TLV::kTLVType_Array)
121 : {
122 25 : str = str + "-" + GetJsonElementStrFromType(subType);
123 : }
124 152 : return str;
125 : }
126 :
127 : TLV::Tag tag;
128 : uint32_t implicitProfileId;
129 : ElementTypeContext type;
130 : ElementTypeContext subType;
131 : };
132 :
133 : /*
134 : * This templated function inserts a name/value pair into the Json object.
135 : * The value is templated to be of type T and accepts any of the following types:
136 : *
137 : * bool, uint*_t, int*_t, char *, float, double, std::string, Json::Value
138 : *
139 : * This method uses the provided element context to generate Json name string.
140 : */
141 : template <typename T>
142 221 : void InsertJsonElement(Json::Value & json, const JsonObjectElementContext & ctx, T val)
143 : {
144 221 : if (json.isArray())
145 : {
146 69 : json.append(val);
147 : }
148 : else
149 : {
150 152 : json[ctx.GenerateJsonElementName()] = val;
151 : }
152 221 : }
153 :
154 : static CHIP_ERROR TlvToJson(TLV::TLVReader & reader, Json::Value & jsonObj);
155 :
156 : /*
157 : * Given a TLVReader positioned at TLV structure this function:
158 : * - enters structure
159 : * - converts all elements of a structure into JSON object representation
160 : * - exits structure
161 : */
162 78 : CHIP_ERROR TlvStructToJson(TLV::TLVReader & reader, Json::Value & jsonObj)
163 : {
164 : CHIP_ERROR err;
165 : TLV::TLVType containerType;
166 :
167 78 : ReturnErrorOnFailure(reader.EnterContainer(containerType));
168 :
169 230 : while ((err = reader.Next()) == CHIP_NO_ERROR)
170 : {
171 155 : TLV::Tag tag = reader.GetTag();
172 158 : VerifyOrReturnError(TLV::IsContextTag(tag) || TLV::IsProfileTag(tag), CHIP_ERROR_INVALID_TLV_TAG);
173 :
174 155 : if (TLV::IsProfileTag(tag) && TLV::VendorIdFromTag(tag) == 0)
175 : {
176 6 : VerifyOrReturnError(TLV::TagNumFromTag(tag) > UINT8_MAX, CHIP_ERROR_INVALID_TLV_TAG);
177 : }
178 :
179 : // Recursively convert to JSON the item within the struct.
180 154 : ReturnErrorOnFailure(TlvToJson(reader, jsonObj));
181 : }
182 :
183 75 : VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
184 75 : return reader.ExitContainer(containerType);
185 : }
186 :
187 223 : CHIP_ERROR TlvToJson(TLV::TLVReader & reader, Json::Value & jsonObj)
188 : {
189 223 : JsonObjectElementContext context(reader);
190 :
191 223 : switch (reader.GetType())
192 : {
193 39 : case TLV::kTLVType_UnsignedInteger: {
194 : uint64_t v;
195 39 : ReturnErrorOnFailure(reader.Get(v));
196 39 : if (CanCastTo<uint32_t>(v))
197 : {
198 33 : InsertJsonElement(jsonObj, context, v);
199 : }
200 : else
201 : {
202 6 : InsertJsonElement(jsonObj, context, std::to_string(v));
203 : }
204 39 : break;
205 : }
206 :
207 43 : case TLV::kTLVType_SignedInteger: {
208 : int64_t v;
209 43 : ReturnErrorOnFailure(reader.Get(v));
210 43 : if (CanCastTo<int32_t>(v))
211 : {
212 37 : InsertJsonElement(jsonObj, context, v);
213 : }
214 : else
215 : {
216 6 : InsertJsonElement(jsonObj, context, std::to_string(v));
217 : }
218 43 : break;
219 : }
220 :
221 21 : case TLV::kTLVType_Boolean: {
222 : bool v;
223 21 : ReturnErrorOnFailure(reader.Get(v));
224 21 : InsertJsonElement(jsonObj, context, v);
225 21 : break;
226 : }
227 :
228 41 : case TLV::kTLVType_FloatingPointNumber: {
229 : double v;
230 41 : ReturnErrorOnFailure(reader.Get(v));
231 41 : if (v == std::numeric_limits<double>::infinity())
232 : {
233 3 : InsertJsonElement(jsonObj, context, kFloatingPointPositiveInfinity);
234 : }
235 38 : else if (v == -std::numeric_limits<double>::infinity())
236 : {
237 3 : InsertJsonElement(jsonObj, context, kFloatingPointNegativeInfinity);
238 : }
239 : else
240 : {
241 35 : InsertJsonElement(jsonObj, context, v);
242 : }
243 41 : break;
244 : }
245 :
246 14 : case TLV::kTLVType_ByteString: {
247 14 : ByteSpan span;
248 14 : ReturnErrorOnFailure(reader.Get(span));
249 :
250 14 : Platform::ScopedMemoryBuffer<char> byteString;
251 14 : byteString.Alloc(BASE64_ENCODED_LEN(span.size()) + 1);
252 14 : VerifyOrReturnError(byteString.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
253 :
254 14 : auto encodedLen = Base64Encode(span.data(), static_cast<uint16_t>(span.size()), byteString.Get());
255 14 : byteString.Get()[encodedLen] = '\0';
256 :
257 14 : InsertJsonElement(jsonObj, context, byteString.Get());
258 14 : break;
259 14 : }
260 :
261 17 : case TLV::kTLVType_UTF8String: {
262 17 : CharSpan span;
263 17 : ReturnErrorOnFailure(reader.Get(span));
264 :
265 17 : std::string str(span.data(), span.size());
266 17 : InsertJsonElement(jsonObj, context, str);
267 17 : break;
268 17 : }
269 :
270 6 : case TLV::kTLVType_Null: {
271 6 : InsertJsonElement(jsonObj, context, Json::Value());
272 6 : break;
273 : }
274 :
275 15 : case TLV::kTLVType_Structure: {
276 15 : Json::Value jsonStruct(Json::objectValue);
277 15 : ReturnErrorOnFailure(TlvStructToJson(reader, jsonStruct));
278 15 : InsertJsonElement(jsonObj, context, jsonStruct);
279 15 : break;
280 15 : }
281 :
282 26 : case TLV::kTLVType_Array: {
283 : CHIP_ERROR err;
284 26 : Json::Value jsonArray(Json::arrayValue);
285 26 : ElementTypeContext prevSubType;
286 26 : ElementTypeContext nextSubType;
287 : TLV::TLVType containerType;
288 :
289 26 : ReturnErrorOnFailure(reader.EnterContainer(containerType));
290 :
291 95 : while ((err = reader.Next()) == CHIP_NO_ERROR)
292 : {
293 70 : VerifyOrReturnError(reader.GetTag() == TLV::AnonymousTag(), CHIP_ERROR_INVALID_TLV_TAG);
294 70 : VerifyOrReturnError(reader.GetType() != TLV::kTLVType_Array, CHIP_ERROR_INVALID_TLV_ELEMENT);
295 :
296 70 : nextSubType.tlvType = reader.GetType();
297 70 : if (nextSubType.tlvType == TLV::kTLVType_FloatingPointNumber)
298 : {
299 12 : nextSubType.isDouble = reader.IsElementDouble();
300 : }
301 :
302 70 : if (jsonArray.empty())
303 : {
304 21 : prevSubType = nextSubType;
305 : }
306 : else
307 : {
308 49 : VerifyOrReturnError(prevSubType.tlvType == nextSubType.tlvType && prevSubType.isDouble == nextSubType.isDouble,
309 : CHIP_ERROR_INVALID_TLV_ELEMENT);
310 : }
311 :
312 : // Recursively convert to JSON the encompassing item within the array.
313 69 : ReturnErrorOnFailure(TlvToJson(reader, jsonArray));
314 : }
315 :
316 25 : VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
317 25 : ReturnErrorOnFailure(reader.ExitContainer(containerType));
318 :
319 25 : context.subType = prevSubType;
320 25 : InsertJsonElement(jsonObj, context, jsonArray);
321 25 : break;
322 26 : }
323 :
324 1 : default:
325 1 : return CHIP_ERROR_INVALID_TLV_ELEMENT;
326 : break;
327 : }
328 :
329 221 : return CHIP_NO_ERROR;
330 : }
331 :
332 : } // namespace
333 :
334 51 : CHIP_ERROR TlvToJson(const ByteSpan & tlv, std::string & jsonString)
335 : {
336 51 : TLV::TLVReader reader;
337 51 : reader.Init(tlv);
338 51 : reader.ImplicitProfileId = kTemporaryImplicitProfileId;
339 :
340 51 : ReturnErrorOnFailure(reader.Next());
341 51 : return TlvToJson(reader, jsonString);
342 : }
343 :
344 65 : CHIP_ERROR TlvToJson(TLV::TLVReader & reader, std::string & jsonString)
345 : {
346 : // The top level element must be a TLV Structure of Anonymous type.
347 65 : VerifyOrReturnError(reader.GetType() == TLV::kTLVType_Structure, CHIP_ERROR_WRONG_TLV_TYPE);
348 64 : VerifyOrReturnError(reader.GetTag() == TLV::AnonymousTag(), CHIP_ERROR_INVALID_TLV_TAG);
349 :
350 : // During json conversion, a implicit profile ID is required
351 63 : ImplicitProfileIdChange implicitProfileIdChange(reader, kTemporaryImplicitProfileId);
352 :
353 63 : Json::Value jsonObject(Json::objectValue);
354 63 : ReturnErrorOnFailure(TlvStructToJson(reader, jsonObject));
355 :
356 60 : Json::StyledWriter writer;
357 60 : jsonString = writer.write(jsonObject);
358 60 : return CHIP_NO_ERROR;
359 63 : }
360 : } // namespace chip
|