Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021-2022 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 "BytesToHex.h"
19 : #include <lib/core/CHIPEncoding.h>
20 : #include <lib/support/CodeUtils.h>
21 :
22 : #include <cstring>
23 : #include <stdio.h>
24 : namespace chip {
25 : namespace Encoding {
26 :
27 : namespace {
28 :
29 141578 : char NibbleToHex(uint8_t nibble, bool uppercase)
30 : {
31 141578 : char x = static_cast<char>(nibble & 0xFu);
32 :
33 141578 : if (x >= 10)
34 : {
35 53738 : return static_cast<char>((x - 10) + (uppercase ? 'A' : 'a'));
36 : }
37 :
38 87840 : return static_cast<char>(x + '0');
39 : }
40 :
41 194987 : CHIP_ERROR MakeU8FromAsciiHex(const char * src, const size_t srcLen, uint8_t * val, BitFlags<HexFlags> flags)
42 : {
43 194987 : if (srcLen != 2)
44 : {
45 0 : return CHIP_ERROR_INVALID_ARGUMENT;
46 : }
47 194987 : uint8_t ret = 0;
48 584922 : for (size_t i = 0; i < srcLen; ++i)
49 : {
50 389962 : ret = static_cast<uint8_t>(ret << 4);
51 389962 : char c = src[i];
52 389962 : uint8_t cval = static_cast<uint8_t>(c);
53 389962 : if (c >= '0' && c <= '9')
54 : {
55 303671 : ret = static_cast<uint8_t>(ret + cval - static_cast<uint8_t>('0'));
56 : }
57 86291 : else if (c >= 'A' && c <= 'F')
58 : {
59 2994 : ret = static_cast<uint8_t>(ret + cval - static_cast<uint8_t>('A') + 0xA);
60 : }
61 : // If kUppercase flag is not set then lowercase are also allowed.
62 83297 : else if (!flags.Has(HexFlags::kUppercase) && c >= 'a' && c <= 'f')
63 : {
64 83270 : ret = static_cast<uint8_t>(ret + cval - static_cast<uint8_t>('a') + 0xA);
65 : }
66 : else
67 : {
68 27 : return CHIP_ERROR_INVALID_ARGUMENT;
69 : }
70 : }
71 194960 : *val = ret;
72 194960 : return CHIP_NO_ERROR;
73 : }
74 :
75 2660 : size_t HexToBytes(const char * src_hex, const size_t src_size, uint8_t * dest_bytes, size_t dest_size_max, BitFlags<HexFlags> flags)
76 : {
77 2660 : if ((src_hex == nullptr) || (dest_bytes == nullptr))
78 : {
79 0 : return 0;
80 : }
81 : // Octet string where each octet is 2 ascii digits representing the hex value
82 : // Each is represented by two ascii chars, so must be even number
83 2660 : if ((src_size & 0x1) != 0 || src_size > dest_size_max * 2)
84 : {
85 1 : return 0;
86 : }
87 :
88 2659 : size_t bytesFilled = 0;
89 197619 : for (size_t i = 0; i < src_size; i += 2)
90 : {
91 194987 : VerifyOrReturnError(MakeU8FromAsciiHex(src_hex + i, 2, &dest_bytes[i / 2], flags) == CHIP_NO_ERROR, 0);
92 194960 : bytesFilled++;
93 : }
94 2632 : return bytesFilled;
95 : }
96 :
97 : } // namespace
98 :
99 8914 : CHIP_ERROR BytesToHex(const uint8_t * src_bytes, size_t src_size, char * dest_hex, size_t dest_size_max, BitFlags<HexFlags> flags)
100 : {
101 8914 : if ((src_bytes == nullptr) && (src_size != 0))
102 : {
103 0 : return CHIP_ERROR_INVALID_ARGUMENT;
104 : }
105 :
106 8914 : if ((dest_hex == nullptr) && (dest_size_max != 0))
107 : {
108 3 : return CHIP_ERROR_INVALID_ARGUMENT;
109 : }
110 :
111 8911 : if (src_size > ((SIZE_MAX - 1) / 2u))
112 : {
113 : // Output would overflow a size_t, let's bail out to avoid computation wraparounds below.
114 : // This condition will hit with slightly less than the very max, but is unlikely to
115 : // ever happen unless an error occurs and won't happen on embedded targets.
116 0 : return CHIP_ERROR_INVALID_ARGUMENT;
117 : }
118 :
119 8911 : bool nul_terminate = flags.Has(HexFlags::kNullTerminate);
120 8911 : size_t expected_output_size = (src_size * 2u) + (nul_terminate ? 1u : 0u);
121 8911 : if (dest_size_max < expected_output_size)
122 : {
123 9 : return CHIP_ERROR_BUFFER_TOO_SMALL;
124 : }
125 :
126 8902 : bool uppercase = flags.Has(HexFlags::kUppercase);
127 8902 : char * cursor = dest_hex;
128 79691 : for (size_t byte_idx = 0; byte_idx < src_size; ++byte_idx)
129 : {
130 70789 : *cursor++ = NibbleToHex((src_bytes[byte_idx] >> 4) & 0xFu, uppercase);
131 70789 : *cursor++ = NibbleToHex((src_bytes[byte_idx] >> 0) & 0xFu, uppercase);
132 : }
133 :
134 8902 : if (nul_terminate)
135 : {
136 47 : *cursor = '\0';
137 : }
138 :
139 8902 : return CHIP_NO_ERROR;
140 : }
141 :
142 8735 : CHIP_ERROR Uint64ToHex(uint64_t src, char * dest_hex, size_t dest_size_max, BitFlags<HexFlags> flags)
143 : {
144 8735 : uint8_t buf[sizeof(src)] = { 0 };
145 8735 : Encoding::BigEndian::Put64(buf, src);
146 8735 : return BytesToHex(buf, sizeof(buf), dest_hex, dest_size_max, flags);
147 : }
148 :
149 123 : CHIP_ERROR Uint32ToHex(uint32_t src, char * dest_hex, size_t dest_size_max, BitFlags<HexFlags> flags)
150 : {
151 123 : uint8_t buf[sizeof(src)] = { 0 };
152 123 : Encoding::BigEndian::Put32(buf, src);
153 123 : return BytesToHex(buf, sizeof(buf), dest_hex, dest_size_max, flags);
154 : }
155 :
156 4 : CHIP_ERROR Uint16ToHex(uint16_t src, char * dest_hex, size_t dest_size_max, BitFlags<HexFlags> flags)
157 : {
158 4 : uint8_t buf[sizeof(src)] = { 0 };
159 4 : Encoding::BigEndian::Put16(buf, src);
160 4 : return BytesToHex(buf, sizeof(buf), dest_hex, dest_size_max, flags);
161 : }
162 :
163 1689 : size_t HexToBytes(const char * src_hex, const size_t src_size, uint8_t * dest_bytes, size_t dest_size_max)
164 : {
165 1689 : return HexToBytes(src_hex, src_size, dest_bytes, dest_size_max, HexFlags::kNone);
166 : }
167 :
168 400 : size_t UppercaseHexToUint64(const char * src_hex, const size_t src_size, uint64_t & dest)
169 : {
170 400 : uint8_t buf[sizeof(uint64_t)] = { 0 };
171 400 : size_t decoded_size = HexToBytes(src_hex, src_size, buf, sizeof(buf), HexFlags::kUppercase);
172 400 : if (decoded_size != sizeof(buf))
173 : {
174 1 : return 0;
175 : }
176 399 : dest = Encoding::BigEndian::Get64(buf);
177 399 : return decoded_size;
178 : }
179 :
180 14 : size_t UppercaseHexToUint32(const char * src_hex, const size_t src_size, uint32_t & dest)
181 : {
182 14 : uint8_t buf[sizeof(uint32_t)] = { 0 };
183 14 : size_t decoded_size = HexToBytes(src_hex, src_size, buf, sizeof(buf), HexFlags::kUppercase);
184 14 : if (decoded_size != sizeof(buf))
185 : {
186 0 : return 0;
187 : }
188 14 : dest = Encoding::BigEndian::Get32(buf);
189 14 : return decoded_size;
190 : }
191 :
192 557 : size_t UppercaseHexToUint16(const char * src_hex, const size_t src_size, uint16_t & dest)
193 : {
194 557 : uint8_t buf[sizeof(uint16_t)] = { 0 };
195 557 : size_t decoded_size = HexToBytes(src_hex, src_size, buf, sizeof(buf), HexFlags::kUppercase);
196 557 : if (decoded_size != sizeof(buf))
197 : {
198 22 : return 0;
199 : }
200 535 : dest = Encoding::BigEndian::Get16(buf);
201 535 : return decoded_size;
202 : }
203 :
204 5 : void LogBufferAsHex(const char * label, const ByteSpan & span)
205 : {
206 5 : constexpr size_t kBytesPerLine = 32u;
207 :
208 5 : size_t remaining = span.size();
209 5 : if (remaining == 0)
210 : {
211 1 : ChipLogProgress(Support, "%s>>>", ((label != nullptr) ? label : ""));
212 1 : return;
213 : }
214 :
215 4 : const uint8_t * cursor = span.data();
216 14 : while (remaining > 0u)
217 : {
218 10 : size_t chunk_size = (remaining < kBytesPerLine) ? remaining : kBytesPerLine;
219 : char hex_buf[(kBytesPerLine * 2) + 1];
220 :
221 10 : CHIP_ERROR err = BytesToUppercaseHexString(cursor, chunk_size, &hex_buf[0], sizeof(hex_buf));
222 10 : if (err != CHIP_NO_ERROR)
223 : {
224 0 : ChipLogProgress(Support, "Failed to dump hex %" CHIP_ERROR_FORMAT, err.Format());
225 0 : return;
226 : }
227 :
228 10 : ChipLogProgress(Support, "%s>>>%s", ((label != nullptr) ? label : ""), hex_buf);
229 :
230 10 : cursor += chunk_size;
231 10 : remaining -= chunk_size;
232 : }
233 : }
234 :
235 : } // namespace Encoding
236 : } // namespace chip
|