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 61 : ImplicitProfileIdChange(TLV::TLVReader & reader, uint32_t id) : mReader(reader), mOldImplicitProfileId(reader.ImplicitProfileId)
43 : {
44 61 : reader.ImplicitProfileId = id;
45 61 : }
46 61 : ~ImplicitProfileIdChange() { mReader.ImplicitProfileId = mOldImplicitProfileId; }
47 :
48 : private:
49 : TLV::TLVReader & mReader;
50 : uint32_t mOldImplicitProfileId;
51 : };
52 :
53 172 : const char * GetJsonElementStrFromType(const ElementTypeContext & ctx)
54 : {
55 172 : switch (ctx.tlvType)
56 : {
57 27 : case TLV::kTLVType_UnsignedInteger:
58 27 : 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 11 : case TLV::kTLVType_ByteString:
66 11 : return kElementTypeBytes;
67 13 : case TLV::kTLVType_UTF8String:
68 13 : return kElementTypeString;
69 5 : case TLV::kTLVType_Null:
70 5 : return kElementTypeNull;
71 12 : case TLV::kTLVType_Structure:
72 12 : 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 218 : JsonObjectElementContext(TLV::TLVReader & reader)
89 218 : {
90 218 : tag = reader.GetTag();
91 218 : implicitProfileId = reader.ImplicitProfileId;
92 218 : type.tlvType = reader.GetType();
93 218 : if (type.tlvType == TLV::kTLVType_FloatingPointNumber)
94 : {
95 41 : type.isDouble = reader.IsElementDouble();
96 : }
97 218 : }
98 :
99 147 : std::string GenerateJsonElementName() const
100 : {
101 147 : std::string str = "???";
102 147 : if (TLV::IsContextTag(tag))
103 : {
104 : // common case for context tags: raw value
105 138 : str = std::to_string(TLV::TagNumFromTag(tag));
106 : }
107 9 : else if (TLV::IsProfileTag(tag))
108 : {
109 9 : if (TLV::ProfileIdFromTag(tag) == implicitProfileId)
110 : {
111 : // Explicit assume implicit tags are just things we want
112 : // 32-bit numbers for
113 9 : str = std::to_string(TLV::TagNumFromTag(tag));
114 : }
115 : else
116 : {
117 : // UNEXPECTED, create a full 64-bit number here
118 0 : str = std::to_string(TLV::ProfileIdFromTag(tag)) + "/" + std::to_string(TLV::TagNumFromTag(tag));
119 : }
120 : }
121 147 : str = str + ":" + GetJsonElementStrFromType(type);
122 147 : if (type.tlvType == TLV::kTLVType_Array)
123 : {
124 25 : str = str + "-" + GetJsonElementStrFromType(subType);
125 : }
126 147 : return str;
127 : }
128 :
129 : TLV::Tag tag;
130 : uint32_t implicitProfileId;
131 : ElementTypeContext type;
132 : ElementTypeContext subType;
133 : };
134 :
135 : /*
136 : * This templated function inserts a name/value pair into the Json object.
137 : * The value is templated to be of type T and accepts any of the following types:
138 : *
139 : * bool, uint*_t, int*_t, char *, float, double, std::string, Json::Value
140 : *
141 : * This method uses the provided element context to generate Json name string.
142 : */
143 : template <typename T>
144 216 : void InsertJsonElement(Json::Value & json, const JsonObjectElementContext & ctx, T val)
145 : {
146 216 : if (json.isArray())
147 : {
148 69 : json.append(val);
149 : }
150 : else
151 : {
152 147 : json[ctx.GenerateJsonElementName()] = val;
153 : }
154 216 : }
155 :
156 : static CHIP_ERROR TlvToJson(TLV::TLVReader & reader, Json::Value & jsonObj);
157 :
158 : /*
159 : * Given a TLVReader positioned at TLV structure this function:
160 : * - enters structure
161 : * - converts all elements of a structure into JSON object representation
162 : * - exits structure
163 : */
164 75 : CHIP_ERROR TlvStructToJson(TLV::TLVReader & reader, Json::Value & jsonObj)
165 : {
166 : CHIP_ERROR err;
167 : TLV::TLVType containerType;
168 :
169 75 : ReturnErrorOnFailure(reader.EnterContainer(containerType));
170 :
171 222 : while ((err = reader.Next()) == CHIP_NO_ERROR)
172 : {
173 150 : TLV::Tag tag = reader.GetTag();
174 153 : VerifyOrReturnError(TLV::IsContextTag(tag) || TLV::IsProfileTag(tag), CHIP_ERROR_INVALID_TLV_TAG);
175 :
176 : // Profile tags are expected to be implicit profile tags and they are
177 : // used to encode > 8bit values from json
178 150 : if (TLV::IsProfileTag(tag))
179 : {
180 10 : VerifyOrReturnError(TLV::ProfileIdFromTag(tag) == reader.ImplicitProfileId, CHIP_ERROR_INVALID_TLV_TAG);
181 9 : VerifyOrReturnError(TLV::TagNumFromTag(tag) > UINT8_MAX, CHIP_ERROR_INVALID_TLV_TAG);
182 : }
183 :
184 : // Recursively convert to JSON the item within the struct.
185 149 : ReturnErrorOnFailure(TlvToJson(reader, jsonObj));
186 : }
187 :
188 72 : VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
189 72 : return reader.ExitContainer(containerType);
190 : }
191 :
192 218 : CHIP_ERROR TlvToJson(TLV::TLVReader & reader, Json::Value & jsonObj)
193 : {
194 218 : JsonObjectElementContext context(reader);
195 :
196 218 : switch (reader.GetType())
197 : {
198 36 : case TLV::kTLVType_UnsignedInteger: {
199 : uint64_t v;
200 36 : ReturnErrorOnFailure(reader.Get(v));
201 36 : if (CanCastTo<uint32_t>(v))
202 : {
203 31 : InsertJsonElement(jsonObj, context, v);
204 : }
205 : else
206 : {
207 5 : InsertJsonElement(jsonObj, context, std::to_string(v));
208 : }
209 36 : break;
210 : }
211 :
212 43 : case TLV::kTLVType_SignedInteger: {
213 : int64_t v;
214 43 : ReturnErrorOnFailure(reader.Get(v));
215 43 : if (CanCastTo<int32_t>(v))
216 : {
217 37 : InsertJsonElement(jsonObj, context, v);
218 : }
219 : else
220 : {
221 6 : InsertJsonElement(jsonObj, context, std::to_string(v));
222 : }
223 43 : break;
224 : }
225 :
226 21 : case TLV::kTLVType_Boolean: {
227 : bool v;
228 21 : ReturnErrorOnFailure(reader.Get(v));
229 21 : InsertJsonElement(jsonObj, context, v);
230 21 : break;
231 : }
232 :
233 41 : case TLV::kTLVType_FloatingPointNumber: {
234 : double v;
235 41 : ReturnErrorOnFailure(reader.Get(v));
236 41 : if (v == std::numeric_limits<double>::infinity())
237 : {
238 3 : InsertJsonElement(jsonObj, context, kFloatingPointPositiveInfinity);
239 : }
240 38 : else if (v == -std::numeric_limits<double>::infinity())
241 : {
242 3 : InsertJsonElement(jsonObj, context, kFloatingPointNegativeInfinity);
243 : }
244 : else
245 : {
246 35 : InsertJsonElement(jsonObj, context, v);
247 : }
248 41 : break;
249 : }
250 :
251 13 : case TLV::kTLVType_ByteString: {
252 13 : ByteSpan span;
253 13 : ReturnErrorOnFailure(reader.Get(span));
254 :
255 13 : Platform::ScopedMemoryBuffer<char> byteString;
256 13 : byteString.Alloc(BASE64_ENCODED_LEN(span.size()) + 1);
257 13 : VerifyOrReturnError(byteString.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
258 :
259 13 : auto encodedLen = Base64Encode(span.data(), static_cast<uint16_t>(span.size()), byteString.Get());
260 13 : byteString.Get()[encodedLen] = '\0';
261 :
262 13 : InsertJsonElement(jsonObj, context, byteString.Get());
263 13 : break;
264 13 : }
265 :
266 17 : case TLV::kTLVType_UTF8String: {
267 17 : CharSpan span;
268 17 : ReturnErrorOnFailure(reader.Get(span));
269 :
270 17 : std::string str(span.data(), span.size());
271 17 : InsertJsonElement(jsonObj, context, str);
272 17 : break;
273 17 : }
274 :
275 6 : case TLV::kTLVType_Null: {
276 6 : InsertJsonElement(jsonObj, context, Json::Value());
277 6 : break;
278 : }
279 :
280 14 : case TLV::kTLVType_Structure: {
281 14 : Json::Value jsonStruct(Json::objectValue);
282 14 : ReturnErrorOnFailure(TlvStructToJson(reader, jsonStruct));
283 14 : InsertJsonElement(jsonObj, context, jsonStruct);
284 14 : break;
285 14 : }
286 :
287 26 : case TLV::kTLVType_Array: {
288 : CHIP_ERROR err;
289 26 : Json::Value jsonArray(Json::arrayValue);
290 26 : ElementTypeContext prevSubType;
291 26 : ElementTypeContext nextSubType;
292 : TLV::TLVType containerType;
293 :
294 26 : ReturnErrorOnFailure(reader.EnterContainer(containerType));
295 :
296 95 : while ((err = reader.Next()) == CHIP_NO_ERROR)
297 : {
298 70 : VerifyOrReturnError(reader.GetTag() == TLV::AnonymousTag(), CHIP_ERROR_INVALID_TLV_TAG);
299 70 : VerifyOrReturnError(reader.GetType() != TLV::kTLVType_Array, CHIP_ERROR_INVALID_TLV_ELEMENT);
300 :
301 70 : nextSubType.tlvType = reader.GetType();
302 70 : if (nextSubType.tlvType == TLV::kTLVType_FloatingPointNumber)
303 : {
304 12 : nextSubType.isDouble = reader.IsElementDouble();
305 : }
306 :
307 70 : if (jsonArray.empty())
308 : {
309 21 : prevSubType = nextSubType;
310 : }
311 : else
312 : {
313 49 : VerifyOrReturnError(prevSubType.tlvType == nextSubType.tlvType && prevSubType.isDouble == nextSubType.isDouble,
314 : CHIP_ERROR_INVALID_TLV_ELEMENT);
315 : }
316 :
317 : // Recursively convert to JSON the encompassing item within the array.
318 69 : ReturnErrorOnFailure(TlvToJson(reader, jsonArray));
319 : }
320 :
321 25 : VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
322 25 : ReturnErrorOnFailure(reader.ExitContainer(containerType));
323 :
324 25 : context.subType = prevSubType;
325 25 : InsertJsonElement(jsonObj, context, jsonArray);
326 25 : break;
327 26 : }
328 :
329 1 : default:
330 1 : return CHIP_ERROR_INVALID_TLV_ELEMENT;
331 : break;
332 : }
333 :
334 216 : return CHIP_NO_ERROR;
335 : }
336 :
337 : } // namespace
338 :
339 49 : CHIP_ERROR TlvToJson(const ByteSpan & tlv, std::string & jsonString)
340 : {
341 : TLV::TLVReader reader;
342 49 : reader.Init(tlv);
343 49 : reader.ImplicitProfileId = kTemporaryImplicitProfileId;
344 :
345 49 : ReturnErrorOnFailure(reader.Next());
346 49 : return TlvToJson(reader, jsonString);
347 : }
348 :
349 63 : CHIP_ERROR TlvToJson(TLV::TLVReader & reader, std::string & jsonString)
350 : {
351 : // The top level element must be a TLV Structure of Anonymous type.
352 63 : VerifyOrReturnError(reader.GetType() == TLV::kTLVType_Structure, CHIP_ERROR_WRONG_TLV_TYPE);
353 62 : VerifyOrReturnError(reader.GetTag() == TLV::AnonymousTag(), CHIP_ERROR_INVALID_TLV_TAG);
354 :
355 : // During json conversion, a implicit profile ID is required
356 61 : ImplicitProfileIdChange implicitProfileIdChange(reader, kTemporaryImplicitProfileId);
357 :
358 61 : Json::Value jsonObject(Json::objectValue);
359 61 : ReturnErrorOnFailure(TlvStructToJson(reader, jsonObject));
360 :
361 58 : Json::StyledWriter writer;
362 58 : jsonString = writer.write(jsonObject);
363 58 : return CHIP_NO_ERROR;
364 61 : }
365 : } // namespace chip
|