LCOV - code coverage report
Current view: top level - lib/support/jsontlv - JsonToTlv.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 217 226 96.0 %
Date: 2024-02-15 08:20:41 Functions: 11 12 91.7 %

          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

Generated by: LCOV version 1.14