LCOV - code coverage report
Current view: top level - lib/support - IniEscaping.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 41 55 74.5 %
Date: 2024-02-15 08:20:41 Functions: 5 7 71.4 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2022 Project CHIP Authors
       4             :  *    All rights reserved.
       5             :  *
       6             :  *    Licensed under the Apache License, Version 2.0 (the "License");
       7             :  *    you may not use this file except in compliance with the License.
       8             :  *    You may obtain a copy of the License at
       9             :  *
      10             :  *        http://www.apache.org/licenses/LICENSE-2.0
      11             :  *
      12             :  *    Unless required by applicable law or agreed to in writing, software
      13             :  *    distributed under the License is distributed on an "AS IS" BASIS,
      14             :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      15             :  *    See the License for the specific language governing permissions and
      16             :  *    limitations under the License.
      17             :  */
      18             : 
      19             : #include <memory>
      20             : #include <string>
      21             : 
      22             : #include "IniEscaping.h"
      23             : #include <lib/support/Base64.h>
      24             : #include <lib/support/BytesToHex.h>
      25             : 
      26             : namespace chip {
      27             : namespace IniEscaping {
      28             : 
      29             : namespace {
      30             : 
      31             : constexpr size_t kEscapeChunkSize = 4; // "\x12" --> 4 chars
      32             : 
      33       10641 : constexpr bool NeedsEscape(char c)
      34             : {
      35       10641 :     return (c <= 0x20) || (c == '=') || (c == '\\') || (c >= 0x7F);
      36             : }
      37             : 
      38        1838 : constexpr bool IsLowercaseHex(char c)
      39             : {
      40        1838 :     return ((c >= '0') && (c <= '9')) || ((c >= 'a') && (c <= 'f'));
      41             : }
      42             : 
      43         919 : bool IsValidEscape(const std::string & s)
      44             : {
      45         919 :     return (s.size() >= kEscapeChunkSize) && (s[0] == '\\') && (s[1] == 'x') && IsLowercaseHex(s[2]) && IsLowercaseHex(s[3]);
      46             : }
      47             : 
      48             : } // namespace
      49             : 
      50         875 : std::string EscapeKey(const std::string & key)
      51             : {
      52         875 :     std::string escapedKey;
      53         875 :     escapedKey.reserve(key.size());
      54             : 
      55       10598 :     for (char c : key)
      56             :     {
      57             :         // Replace spaces, non-printable chars, `=` and the escape itself with hex-escaped (C-style) characters.
      58        9723 :         if (NeedsEscape(c))
      59             :         {
      60         877 :             char escaped[kEscapeChunkSize + 1] = { 0 };
      61         877 :             snprintf(escaped, sizeof(escaped), "\\x%02x", (static_cast<unsigned>(c) & 0xff));
      62         877 :             escapedKey += escaped;
      63             :         }
      64             :         else
      65             :         {
      66        8846 :             escapedKey += c;
      67             :         }
      68             :     }
      69             : 
      70         875 :     return escapedKey;
      71             : }
      72             : 
      73         285 : std::string UnescapeKey(const std::string & key)
      74             : {
      75         285 :     std::string unescaped;
      76         285 :     unescaped.reserve(key.size());
      77             : 
      78         285 :     size_t idx       = 0;
      79         285 :     size_t remaining = key.size();
      80        1708 :     while (remaining > 0)
      81             :     {
      82        1427 :         char c = key[idx];
      83        1427 :         if (c == '\\')
      84             :         {
      85             :             // Don't process invalid escapes.
      86         920 :             if (remaining < kEscapeChunkSize)
      87             :             {
      88           1 :                 return "";
      89             :             }
      90             : 
      91         919 :             auto escapeChunk = key.substr(idx, kEscapeChunkSize);
      92         919 :             if (!IsValidEscape(escapeChunk))
      93             :             {
      94           1 :                 return "";
      95             :             }
      96             : 
      97             :             // We validated format, now extract the last two chars as hex
      98         918 :             auto hexDigits   = escapeChunk.substr(2, 2);
      99         918 :             uint8_t charByte = 0;
     100         918 :             if ((chip::Encoding::HexToBytes(hexDigits.data(), 2, &charByte, 1) != 1) || !NeedsEscape(static_cast<char>(charByte)))
     101             :             {
     102           2 :                 return "";
     103             :             }
     104             : 
     105         916 :             unescaped += static_cast<char>(charByte);
     106         916 :             idx += kEscapeChunkSize;
     107         921 :         }
     108             :         else
     109             :         {
     110         507 :             unescaped += c;
     111         507 :             idx += 1;
     112             :         }
     113             : 
     114        1423 :         remaining = key.size() - idx;
     115             :     }
     116             : 
     117         281 :     return unescaped;
     118         285 : }
     119             : 
     120           0 : std::string StringToBase64(const std::string & value)
     121             : {
     122           0 :     std::unique_ptr<char[]> buffer(new char[BASE64_ENCODED_LEN(value.length())]);
     123             : 
     124             :     uint32_t len =
     125           0 :         chip::Base64Encode32(reinterpret_cast<const uint8_t *>(value.data()), static_cast<uint32_t>(value.length()), buffer.get());
     126           0 :     if (len == UINT32_MAX)
     127             :     {
     128           0 :         return "";
     129             :     }
     130             : 
     131           0 :     return std::string(buffer.get(), len);
     132           0 : }
     133             : 
     134           0 : std::string Base64ToString(const std::string & b64Value)
     135             : {
     136           0 :     std::unique_ptr<uint8_t[]> buffer(new uint8_t[BASE64_MAX_DECODED_LEN(b64Value.length())]);
     137             : 
     138           0 :     uint32_t len = chip::Base64Decode32(b64Value.data(), static_cast<uint32_t>(b64Value.length()), buffer.get());
     139           0 :     if (len == UINT32_MAX)
     140             :     {
     141           0 :         return "";
     142             :     }
     143             : 
     144           0 :     return std::string(reinterpret_cast<const char *>(buffer.get()), len);
     145           0 : }
     146             : 
     147             : } // namespace IniEscaping
     148             : } // namespace chip

Generated by: LCOV version 1.14