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/ScopedMemoryBuffer.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 64 : ImplicitProfileIdChange(TLV::TLVReader & reader, uint32_t id) : mReader(reader), mOldImplicitProfileId(reader.ImplicitProfileId)
43 : {
44 64 : reader.ImplicitProfileId = id;
45 64 : }
46 64 : ~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 224 : JsonObjectElementContext(TLV::TLVReader & reader)
89 224 : {
90 224 : tag = reader.GetTag();
91 224 : implicitProfileId = reader.ImplicitProfileId;
92 224 : type.tlvType = reader.GetType();
93 224 : if (type.tlvType == TLV::kTLVType_FloatingPointNumber)
94 : {
95 41 : type.isDouble = reader.IsElementDouble();
96 : }
97 224 : }
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 79 : CHIP_ERROR TlvStructToJson(TLV::TLVReader & reader, Json::Value & jsonObj)
163 : {
164 : CHIP_ERROR err;
165 : TLV::TLVType containerType;
166 :
167 79 : ReturnErrorOnFailure(reader.EnterContainer(containerType));
168 :
169 462 : while ((err = reader.Next()) == CHIP_NO_ERROR)
170 : {
171 156 : TLV::Tag tag = reader.GetTag();
172 156 : VerifyOrReturnError(TLV::IsContextTag(tag) || TLV::IsProfileTag(tag), CHIP_ERROR_INVALID_TLV_TAG);
173 :
174 156 : 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 155 : ReturnErrorOnFailure(TlvToJson(reader, jsonObj));
181 : }
182 :
183 150 : VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
184 75 : return reader.ExitContainer(containerType);
185 : }
186 :
187 224 : CHIP_ERROR TlvToJson(TLV::TLVReader & reader, Json::Value & jsonObj)
188 : {
189 224 : JsonObjectElementContext context(reader);
190 :
191 224 : 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 15 : case TLV::kTLVType_ByteString: {
247 15 : ByteSpan span;
248 15 : ReturnErrorOnFailure(reader.Get(span));
249 :
250 : // Base64Encode takes and returns a uint16_t length, so a byte string whose raw or
251 : // base64-encoded size exceeds 65535 would be silently truncated instead of encoded.
252 : // Reject it, matching the guard the sibling Json<->TLV converters already apply.
253 15 : VerifyOrReturnError(CanCastTo<uint16_t>(span.size()), CHIP_ERROR_INVALID_TLV_ELEMENT);
254 15 : VerifyOrReturnError(CanCastTo<uint16_t>(BASE64_ENCODED_LEN(span.size())), CHIP_ERROR_INVALID_TLV_ELEMENT);
255 :
256 14 : Platform::ScopedMemoryBuffer<char> byteString;
257 14 : byteString.Alloc(BASE64_ENCODED_LEN(span.size()) + 1);
258 14 : VerifyOrReturnError(byteString.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
259 :
260 14 : auto encodedLen = Base64Encode(span.data(), static_cast<uint16_t>(span.size()), byteString.Get());
261 14 : byteString.Get()[encodedLen] = '\0';
262 :
263 14 : InsertJsonElement(jsonObj, context, byteString.Get());
264 14 : break;
265 14 : }
266 :
267 17 : case TLV::kTLVType_UTF8String: {
268 17 : CharSpan span;
269 17 : ReturnErrorOnFailure(reader.Get(span));
270 :
271 17 : std::string str(span.data(), span.size());
272 17 : InsertJsonElement(jsonObj, context, str);
273 17 : break;
274 17 : }
275 :
276 6 : case TLV::kTLVType_Null: {
277 6 : InsertJsonElement(jsonObj, context, Json::Value());
278 6 : break;
279 : }
280 :
281 15 : case TLV::kTLVType_Structure: {
282 15 : Json::Value jsonStruct(Json::objectValue);
283 15 : ReturnErrorOnFailure(TlvStructToJson(reader, jsonStruct));
284 15 : InsertJsonElement(jsonObj, context, jsonStruct);
285 15 : break;
286 15 : }
287 :
288 26 : case TLV::kTLVType_Array: {
289 : CHIP_ERROR err;
290 26 : Json::Value jsonArray(Json::arrayValue);
291 26 : ElementTypeContext prevSubType;
292 26 : ElementTypeContext nextSubType;
293 : TLV::TLVType containerType;
294 :
295 26 : ReturnErrorOnFailure(reader.EnterContainer(containerType));
296 :
297 190 : while ((err = reader.Next()) == CHIP_NO_ERROR)
298 : {
299 70 : VerifyOrReturnError(reader.GetTag() == TLV::AnonymousTag(), CHIP_ERROR_INVALID_TLV_TAG);
300 70 : VerifyOrReturnError(reader.GetType() != TLV::kTLVType_Array, CHIP_ERROR_INVALID_TLV_ELEMENT);
301 :
302 70 : nextSubType.tlvType = reader.GetType();
303 70 : if (nextSubType.tlvType == TLV::kTLVType_FloatingPointNumber)
304 : {
305 12 : nextSubType.isDouble = reader.IsElementDouble();
306 : }
307 :
308 70 : if (jsonArray.empty())
309 : {
310 21 : prevSubType = nextSubType;
311 : }
312 : else
313 : {
314 49 : VerifyOrReturnError(prevSubType.tlvType == nextSubType.tlvType && prevSubType.isDouble == nextSubType.isDouble,
315 : CHIP_ERROR_INVALID_TLV_ELEMENT);
316 : }
317 :
318 : // Recursively convert to JSON the encompassing item within the array.
319 69 : ReturnErrorOnFailure(TlvToJson(reader, jsonArray));
320 : }
321 :
322 50 : VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
323 25 : ReturnErrorOnFailure(reader.ExitContainer(containerType));
324 :
325 25 : context.subType = prevSubType;
326 25 : InsertJsonElement(jsonObj, context, jsonArray);
327 25 : break;
328 26 : }
329 :
330 1 : default:
331 1 : return CHIP_ERROR_INVALID_TLV_ELEMENT;
332 : break;
333 : }
334 :
335 221 : return CHIP_NO_ERROR;
336 : }
337 :
338 : } // namespace
339 :
340 52 : CHIP_ERROR TlvToJson(const ByteSpan & tlv, std::string & jsonString)
341 : {
342 52 : TLV::TLVReader reader;
343 52 : reader.Init(tlv);
344 52 : reader.ImplicitProfileId = kTemporaryImplicitProfileId;
345 :
346 52 : ReturnErrorOnFailure(reader.Next());
347 52 : return TlvToJson(reader, jsonString);
348 : }
349 :
350 66 : CHIP_ERROR TlvToJson(TLV::TLVReader & reader, std::string & jsonString)
351 : {
352 : // The top level element must be a TLV Structure of Anonymous type.
353 66 : VerifyOrReturnError(reader.GetType() == TLV::kTLVType_Structure, CHIP_ERROR_WRONG_TLV_TYPE);
354 65 : VerifyOrReturnError(reader.GetTag() == TLV::AnonymousTag(), CHIP_ERROR_INVALID_TLV_TAG);
355 :
356 : // During json conversion, a implicit profile ID is required
357 64 : ImplicitProfileIdChange implicitProfileIdChange(reader, kTemporaryImplicitProfileId);
358 :
359 64 : Json::Value jsonObject(Json::objectValue);
360 64 : ReturnErrorOnFailure(TlvStructToJson(reader, jsonObject));
361 :
362 60 : Json::StyledWriter writer;
363 60 : jsonString = writer.write(jsonObject);
364 60 : return CHIP_NO_ERROR;
365 64 : }
366 : } // namespace chip
|