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