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 <stdint.h>
19 :
20 : #include <algorithm>
21 : #include <charconv>
22 : #include <sstream>
23 : #include <string>
24 : #include <vector>
25 :
26 : #include <json/json.h>
27 : #include <lib/support/Base64.h>
28 : #include <lib/support/SafeInt.h>
29 : #include <lib/support/jsontlv/ElementTypes.h>
30 : #include <lib/support/jsontlv/JsonToTlv.h>
31 :
32 : namespace chip {
33 :
34 : namespace {
35 :
36 : // Not directly used: TLV encoding will not encode this number and
37 : // will just encode "Implicit profile tag"
38 : // This profile, but will be used for deciding what binary values to encode.
39 : constexpr uint32_t kTemporaryImplicitProfileId = 0xFF01;
40 :
41 337 : std::vector<std::string> SplitIntoFieldsBySeparator(const std::string & input, char separator)
42 : {
43 337 : std::vector<std::string> substrings;
44 337 : std::stringstream ss(input);
45 337 : std::string substring;
46 :
47 1074 : while (std::getline(ss, substring, separator))
48 : {
49 737 : substrings.push_back(std::move(substring));
50 : }
51 :
52 337 : return substrings;
53 337 : }
54 :
55 325 : CHIP_ERROR JsonTypeStrToTlvType(const char * elementType, ElementTypeContext & type)
56 : {
57 325 : if (strcmp(elementType, kElementTypeInt) == 0)
58 : {
59 55 : type.tlvType = TLV::kTLVType_SignedInteger;
60 : }
61 270 : else if (strcmp(elementType, kElementTypeUInt) == 0)
62 : {
63 50 : type.tlvType = TLV::kTLVType_UnsignedInteger;
64 : }
65 220 : else if (strcmp(elementType, kElementTypeBool) == 0)
66 : {
67 30 : type.tlvType = TLV::kTLVType_Boolean;
68 : }
69 190 : else if (strcmp(elementType, kElementTypeFloat) == 0)
70 : {
71 29 : type.tlvType = TLV::kTLVType_FloatingPointNumber;
72 29 : type.isDouble = false;
73 : }
74 161 : else if (strcmp(elementType, kElementTypeDouble) == 0)
75 : {
76 29 : type.tlvType = TLV::kTLVType_FloatingPointNumber;
77 29 : type.isDouble = true;
78 : }
79 132 : else if (strcmp(elementType, kElementTypeBytes) == 0)
80 : {
81 28 : type.tlvType = TLV::kTLVType_ByteString;
82 : }
83 104 : else if (strcmp(elementType, kElementTypeString) == 0)
84 : {
85 22 : type.tlvType = TLV::kTLVType_UTF8String;
86 : }
87 82 : else if (strcmp(elementType, kElementTypeNull) == 0)
88 : {
89 8 : type.tlvType = TLV::kTLVType_Null;
90 : }
91 74 : else if (strcmp(elementType, kElementTypeStruct) == 0)
92 : {
93 24 : type.tlvType = TLV::kTLVType_Structure;
94 : }
95 50 : else if (strncmp(elementType, kElementTypeArray, strlen(kElementTypeArray)) == 0)
96 : {
97 49 : type.tlvType = TLV::kTLVType_Array;
98 : }
99 : else
100 : {
101 1 : return CHIP_ERROR_INVALID_ARGUMENT;
102 : }
103 :
104 324 : return CHIP_NO_ERROR;
105 : }
106 :
107 : struct ElementContext
108 : {
109 : std::string jsonName;
110 : TLV::Tag tag = TLV::AnonymousTag();
111 : ElementTypeContext type;
112 : ElementTypeContext subType;
113 : };
114 :
115 322 : bool CompareByTag(const ElementContext & a, const ElementContext & b)
116 : {
117 : // If tags are of the same type compare by tag number
118 322 : if (IsContextTag(a.tag) == IsContextTag(b.tag))
119 : {
120 310 : return TLV::TagNumFromTag(a.tag) < TLV::TagNumFromTag(b.tag);
121 : }
122 : // Otherwise, compare by tag type: context tags first followed by common profile tags
123 12 : return IsContextTag(a.tag);
124 : }
125 :
126 : // The profileId parameter is used when encoding a tag for a TLV element to specify the profile that the tag belongs to.
127 : // If the vendor ID is zero but the tag ID does not fit within an 8-bit value, the function uses Implicit Profile Tag.
128 : // Here, the kTemporaryImplicitProfileId serves as a default value for cases where no explicit profile ID is provided by
129 : // the caller. This allows for the encoding of tags that are not vendor-specific or context-specific but are instead
130 : // associated with a temporary implicit profile ID (0xFF01).
131 285 : CHIP_ERROR InternalConvertTlvTag(uint32_t tagNumber, TLV::Tag & tag, const uint32_t profileId = kTemporaryImplicitProfileId)
132 : {
133 285 : uint16_t vendor_id = static_cast<uint16_t>(tagNumber >> 16);
134 285 : uint16_t tag_id = static_cast<uint16_t>(tagNumber & 0xFFFF);
135 :
136 285 : if (vendor_id != 0)
137 : {
138 18 : tag = TLV::ProfileTag(vendor_id, /*profileNum=*/0, tag_id);
139 : }
140 267 : else if (tag_id <= UINT8_MAX)
141 : {
142 256 : tag = TLV::ContextTag(static_cast<uint8_t>(tagNumber));
143 : }
144 : else
145 : {
146 11 : tag = TLV::ProfileTag(profileId, tagNumber);
147 : }
148 285 : return CHIP_NO_ERROR;
149 : }
150 :
151 : template <typename T>
152 305 : CHIP_ERROR ParseNumericalField(const std::string & decimalString, T & outValue)
153 : {
154 305 : const char * start_ptr = decimalString.data();
155 305 : const char * end_ptr = decimalString.data() + decimalString.size();
156 305 : auto [last_converted_ptr, _] = std::from_chars(start_ptr, end_ptr, outValue, 10);
157 305 : VerifyOrReturnError(last_converted_ptr == end_ptr, CHIP_ERROR_INVALID_ARGUMENT);
158 303 : return CHIP_NO_ERROR;
159 : }
160 :
161 288 : CHIP_ERROR ParseJsonName(const std::string & name, ElementContext & elementCtx, uint32_t implicitProfileId)
162 : {
163 288 : uint32_t tagNumber = 0;
164 288 : const char * elementType = nullptr;
165 288 : std::vector<std::string> nameFields = SplitIntoFieldsBySeparator(name, ':');
166 288 : TLV::Tag tag = TLV::AnonymousTag();
167 288 : ElementTypeContext type;
168 288 : ElementTypeContext subType;
169 :
170 288 : if (nameFields.size() == 2)
171 : {
172 223 : ReturnErrorOnFailure(ParseNumericalField(nameFields[0], tagNumber));
173 221 : elementType = nameFields[1].c_str();
174 : }
175 65 : else if (nameFields.size() == 3)
176 : {
177 64 : ReturnErrorOnFailure(ParseNumericalField(nameFields[1], tagNumber));
178 64 : elementType = nameFields[2].c_str();
179 : }
180 : else
181 : {
182 1 : return CHIP_ERROR_INVALID_ARGUMENT;
183 : }
184 :
185 285 : ReturnErrorOnFailure(InternalConvertTlvTag(tagNumber, tag, implicitProfileId));
186 285 : ReturnErrorOnFailure(JsonTypeStrToTlvType(elementType, type));
187 :
188 284 : if (type.tlvType == TLV::kTLVType_Array)
189 : {
190 98 : std::vector<std::string> arrayFields = SplitIntoFieldsBySeparator(elementType, '-');
191 49 : VerifyOrReturnError(arrayFields.size() == 2, CHIP_ERROR_INVALID_ARGUMENT);
192 :
193 49 : if (strcmp(arrayFields[1].c_str(), kElementTypeEmpty) == 0)
194 : {
195 9 : subType.tlvType = TLV::kTLVType_NotSpecified;
196 : }
197 : else
198 : {
199 40 : ReturnErrorOnFailure(JsonTypeStrToTlvType(arrayFields[1].c_str(), subType));
200 : }
201 49 : }
202 :
203 284 : elementCtx.jsonName = name;
204 284 : elementCtx.tag = tag;
205 284 : elementCtx.type = type;
206 284 : elementCtx.subType = subType;
207 :
208 284 : return CHIP_NO_ERROR;
209 288 : }
210 :
211 545 : CHIP_ERROR EncodeTlvElement(const Json::Value & val, TLV::TLVWriter & writer, const ElementContext & elementCtx)
212 : {
213 545 : TLV::Tag tag = elementCtx.tag;
214 :
215 545 : switch (elementCtx.type.tlvType)
216 : {
217 64 : case TLV::kTLVType_UnsignedInteger: {
218 64 : uint64_t v = 0;
219 64 : if (val.isUInt64())
220 : {
221 53 : v = val.asUInt64();
222 : }
223 11 : else if (val.isString())
224 : {
225 10 : ReturnErrorOnFailure(ParseNumericalField(val.asString(), v));
226 : }
227 : else
228 : {
229 1 : return CHIP_ERROR_INVALID_ARGUMENT;
230 : }
231 63 : ReturnErrorOnFailure(writer.Put(tag, v));
232 63 : break;
233 : }
234 :
235 91 : case TLV::kTLVType_SignedInteger: {
236 91 : int64_t v = 0;
237 91 : if (val.isInt64())
238 : {
239 83 : v = val.asInt64();
240 : }
241 8 : else if (val.isString())
242 : {
243 8 : ReturnErrorOnFailure(ParseNumericalField(val.asString(), v));
244 : }
245 : else
246 : {
247 0 : return CHIP_ERROR_INVALID_ARGUMENT;
248 : }
249 91 : ReturnErrorOnFailure(writer.Put(tag, v));
250 91 : break;
251 : }
252 :
253 38 : case TLV::kTLVType_Boolean: {
254 38 : VerifyOrReturnError(val.isBool(), CHIP_ERROR_INVALID_ARGUMENT);
255 37 : ReturnErrorOnFailure(writer.Put(tag, val.asBool()));
256 37 : break;
257 : }
258 :
259 76 : case TLV::kTLVType_FloatingPointNumber: {
260 76 : if (val.isNumeric())
261 : {
262 62 : if (elementCtx.type.isDouble)
263 : {
264 36 : ReturnErrorOnFailure(writer.Put(tag, val.asDouble()));
265 : }
266 : else
267 : {
268 26 : ReturnErrorOnFailure(writer.Put(tag, val.asFloat()));
269 : }
270 : }
271 14 : else if (val.isString())
272 : {
273 14 : const std::string valAsString = val.asString();
274 14 : bool isPositiveInfinity = (valAsString == kFloatingPointPositiveInfinity);
275 14 : bool isNegativeInfinity = (valAsString == kFloatingPointNegativeInfinity);
276 14 : VerifyOrReturnError(isPositiveInfinity || isNegativeInfinity, CHIP_ERROR_INVALID_ARGUMENT);
277 12 : if (elementCtx.type.isDouble)
278 : {
279 6 : if (isPositiveInfinity)
280 : {
281 4 : ReturnErrorOnFailure(writer.Put(tag, std::numeric_limits<double>::infinity()));
282 : }
283 : else
284 : {
285 2 : ReturnErrorOnFailure(writer.Put(tag, -std::numeric_limits<double>::infinity()));
286 : }
287 : }
288 : else
289 : {
290 6 : if (isPositiveInfinity)
291 : {
292 2 : ReturnErrorOnFailure(writer.Put(tag, std::numeric_limits<float>::infinity()));
293 : }
294 : else
295 : {
296 4 : ReturnErrorOnFailure(writer.Put(tag, -std::numeric_limits<float>::infinity()));
297 : }
298 : }
299 14 : }
300 : else
301 : {
302 0 : return CHIP_ERROR_INVALID_ARGUMENT;
303 : }
304 74 : break;
305 : }
306 :
307 32 : case TLV::kTLVType_ByteString: {
308 39 : VerifyOrReturnError(val.isString(), CHIP_ERROR_INVALID_ARGUMENT);
309 32 : const std::string valAsString = val.asString();
310 32 : size_t encodedLen = valAsString.length();
311 32 : VerifyOrReturnError(CanCastTo<uint16_t>(encodedLen), CHIP_ERROR_INVALID_ARGUMENT);
312 :
313 : // Check if the length is a multiple of 4 as strict padding is required.
314 32 : VerifyOrReturnError(encodedLen % 4 == 0, CHIP_ERROR_INVALID_ARGUMENT);
315 :
316 26 : Platform::ScopedMemoryBuffer<uint8_t> byteString;
317 26 : byteString.Alloc(BASE64_MAX_DECODED_LEN(static_cast<uint16_t>(encodedLen)));
318 26 : VerifyOrReturnError(byteString.Get() != nullptr, CHIP_ERROR_NO_MEMORY);
319 :
320 26 : auto decodedLen = Base64Decode(valAsString.c_str(), static_cast<uint16_t>(encodedLen), byteString.Get());
321 26 : VerifyOrReturnError(decodedLen < UINT16_MAX, CHIP_ERROR_INVALID_ARGUMENT);
322 25 : ReturnErrorOnFailure(writer.PutBytes(tag, byteString.Get(), decodedLen));
323 25 : break;
324 58 : }
325 :
326 30 : case TLV::kTLVType_UTF8String: {
327 30 : VerifyOrReturnError(val.isString(), CHIP_ERROR_INVALID_ARGUMENT);
328 30 : const std::string valAsString = val.asString();
329 30 : ReturnErrorOnFailure(writer.PutString(tag, valAsString.data(), static_cast<uint32_t>(valAsString.size())));
330 30 : break;
331 30 : }
332 :
333 10 : case TLV::kTLVType_Null: {
334 10 : VerifyOrReturnError(val.isNull(), CHIP_ERROR_INVALID_ARGUMENT);
335 10 : ReturnErrorOnFailure(writer.PutNull(tag));
336 10 : break;
337 : }
338 :
339 155 : case TLV::kTLVType_Structure: {
340 : TLV::TLVType containerType;
341 170 : VerifyOrReturnError(val.isObject(), CHIP_ERROR_INVALID_ARGUMENT);
342 155 : ReturnErrorOnFailure(writer.StartContainer(tag, TLV::kTLVType_Structure, containerType));
343 :
344 155 : std::vector<std::string> jsonNames = val.getMemberNames();
345 155 : std::vector<ElementContext> nestedElementsCtx;
346 :
347 439 : for (size_t i = 0; i < jsonNames.size(); i++)
348 : {
349 288 : ElementContext ctx;
350 288 : ReturnErrorOnFailure(ParseJsonName(jsonNames[i], ctx, writer.ImplicitProfileId));
351 284 : nestedElementsCtx.push_back(ctx);
352 288 : }
353 :
354 : // Sort Json object elements by Tag number (low to high).
355 : // Note that all sorted Context Tags will appear first followed by all sorted Common Tags.
356 151 : std::sort(nestedElementsCtx.begin(), nestedElementsCtx.end(), CompareByTag);
357 :
358 424 : for (auto & ctx : nestedElementsCtx)
359 : {
360 284 : ReturnErrorOnFailure(EncodeTlvElement(val[ctx.jsonName], writer, ctx));
361 : }
362 :
363 140 : ReturnErrorOnFailure(writer.EndContainer(containerType));
364 140 : break;
365 310 : }
366 :
367 49 : case TLV::kTLVType_Array: {
368 : TLV::TLVType containerType;
369 51 : VerifyOrReturnError(val.isArray(), CHIP_ERROR_INVALID_ARGUMENT);
370 49 : ReturnErrorOnFailure(writer.StartContainer(tag, TLV::kTLVType_Array, containerType));
371 :
372 49 : if (elementCtx.subType.tlvType == TLV::kTLVType_NotSpecified)
373 : {
374 9 : VerifyOrReturnError(val.size() == 0, CHIP_ERROR_INVALID_ARGUMENT);
375 : }
376 : else
377 : {
378 40 : ElementContext nestedElementCtx;
379 40 : nestedElementCtx.tag = TLV::AnonymousTag();
380 40 : nestedElementCtx.type = elementCtx.subType;
381 171 : for (Json::ArrayIndex i = 0; i < val.size(); i++)
382 : {
383 133 : ReturnErrorOnFailure(EncodeTlvElement(val[i], writer, nestedElementCtx));
384 : }
385 40 : }
386 :
387 47 : ReturnErrorOnFailure(writer.EndContainer(containerType));
388 47 : break;
389 : }
390 :
391 0 : default:
392 0 : return CHIP_ERROR_INVALID_TLV_ELEMENT;
393 : break;
394 : }
395 :
396 517 : return CHIP_NO_ERROR;
397 : }
398 :
399 : } // namespace
400 :
401 108 : CHIP_ERROR JsonToTlv(const std::string & jsonString, MutableByteSpan & tlv)
402 : {
403 108 : TLV::TLVWriter writer;
404 108 : writer.Init(tlv);
405 108 : writer.ImplicitProfileId = kTemporaryImplicitProfileId;
406 108 : ReturnErrorOnFailure(JsonToTlv(jsonString, writer));
407 92 : ReturnErrorOnFailure(writer.Finalize());
408 92 : tlv.reduce_size(writer.GetLengthWritten());
409 92 : return CHIP_NO_ERROR;
410 : }
411 :
412 129 : CHIP_ERROR JsonToTlv(const std::string & jsonString, TLV::TLVWriter & writer)
413 : {
414 129 : Json::Reader reader;
415 129 : Json::Value json;
416 129 : bool result = reader.parse(jsonString, json);
417 129 : VerifyOrReturnError(result, CHIP_ERROR_INTERNAL);
418 :
419 128 : ElementContext elementCtx;
420 128 : elementCtx.type = { TLV::kTLVType_Structure, false };
421 :
422 : // Use kTemporaryImplicitProfileId as the default value for cases where no explicit implicit profile ID is provided by
423 : // the caller. This allows for the encoding of tags that are not vendor-specific or context-specific but are instead
424 : // associated with a temporary implicit profile ID (0xFF01).
425 128 : if (writer.ImplicitProfileId == TLV::kProfileIdNotSpecified)
426 : {
427 0 : writer.ImplicitProfileId = kTemporaryImplicitProfileId;
428 : }
429 :
430 128 : return EncodeTlvElement(json, writer, elementCtx);
431 129 : }
432 :
433 0 : CHIP_ERROR ConvertTlvTag(uint32_t tagNumber, TLV::Tag & tag)
434 : {
435 0 : return InternalConvertTlvTag(tagNumber, tag);
436 : }
437 : } // namespace chip
|