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