Coverage Report

Created: 2025-12-31 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/botan/src/lib/codec/hex/hex.cpp
Line
Count
Source
1
/*
2
* Hex Encoding and Decoding
3
* (C) 2010,2020 Jack Lloyd
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7
8
#include <botan/hex.h>
9
10
#include <botan/exceptn.h>
11
#include <botan/mem_ops.h>
12
#include <botan/internal/charset.h>
13
#include <botan/internal/ct_utils.h>
14
#include <botan/internal/fmt.h>
15
#include <botan/internal/int_utils.h>
16
#include <botan/internal/loadstor.h>
17
18
namespace Botan {
19
20
namespace {
21
22
103k
uint16_t hex_encode_2nibble(uint8_t n8, bool uppercase) {
23
   // Offset for upper or lower case 'a' resp
24
103k
   const uint16_t a_mask = uppercase ? 0x0707 : 0x2727;
25
26
103k
   const uint16_t n = (static_cast<uint16_t>(n8 & 0xF0) << 4) | (n8 & 0x0F);
27
   // n >= 10? If so add offset
28
103k
   const uint16_t diff = swar_lt<uint16_t>(0x0909, n) & a_mask;
29
   // Can't overflow between bytes, so don't need explicit SWAR addition:
30
103k
   return n + 0x3030 + diff;
31
103k
}
32
33
}  // namespace
34
35
2.31k
void hex_encode(char output[], const uint8_t input[], size_t input_length, bool uppercase) {
36
105k
   for(size_t i = 0; i != input_length; ++i) {
37
103k
      const uint16_t h = hex_encode_2nibble(input[i], uppercase);
38
103k
      output[2 * i] = get_byte<0>(h);
39
103k
      output[2 * i + 1] = get_byte<1>(h);
40
103k
   }
41
2.31k
}
42
43
2.31k
std::string hex_encode(const uint8_t input[], size_t input_length, bool uppercase) {
44
2.31k
   std::string output(2 * input_length, 0);
45
46
2.31k
   if(input_length > 0) {
47
2.31k
      hex_encode(&output.front(), input, input_length, uppercase);
48
2.31k
   }
49
50
2.31k
   return output;
51
2.31k
}
52
53
namespace {
54
55
576
uint8_t hex_char_to_bin(char input) {
56
   // Starts of valid value ranges (v_lo) and their lengths (v_range)
57
576
   constexpr uint64_t v_lo = make_uint64(0, '0', 'a', 'A', ' ', '\n', '\t', '\r');
58
576
   constexpr uint64_t v_range = make_uint64(0, 10, 6, 6, 1, 1, 1, 1);
59
60
576
   const uint8_t x = static_cast<uint8_t>(input);
61
576
   const uint64_t x8 = x * 0x0101010101010101;
62
63
576
   const uint64_t v_mask = swar_in_range<uint64_t>(x8, v_lo, v_range) ^ 0x8000000000000000;
64
65
   // This is the offset added to x to get the value we need
66
576
   const uint64_t val_v = 0xd0a9c960767773 ^ static_cast<uint64_t>(0xFF - x) << 56;
67
68
576
   return x + static_cast<uint8_t>(val_v >> (8 * index_of_first_set_byte(v_mask)));
69
576
}
70
71
}  // namespace
72
73
6
size_t hex_decode(uint8_t output[], const char input[], size_t input_length, size_t& input_consumed, bool ignore_ws) {
74
6
   uint8_t* out_ptr = output;
75
6
   bool top_nibble = true;
76
77
6
   clear_mem(output, input_length / 2);
78
79
582
   for(size_t i = 0; i != input_length; ++i) {
80
576
      const uint8_t bin = hex_char_to_bin(input[i]);
81
82
576
      if(bin >= 0x10) {
83
0
         if(bin == 0x80 && ignore_ws) {
84
0
            continue;
85
0
         }
86
87
0
         throw Invalid_Argument(fmt("hex_decode: invalid character '{}'", format_char_for_display(input[i])));
88
0
      }
89
90
576
      if(top_nibble) {
91
288
         *out_ptr |= bin << 4;
92
288
      } else {
93
288
         *out_ptr |= bin;
94
288
      }
95
96
576
      top_nibble = !top_nibble;
97
576
      if(top_nibble) {
98
288
         ++out_ptr;
99
288
      }
100
576
   }
101
102
6
   input_consumed = input_length;
103
6
   size_t written = (out_ptr - output);
104
105
   /*
106
   * We only got half of a uint8_t at the end; zap the half-written
107
   * output and mark it as unread
108
   */
109
6
   if(!top_nibble) {
110
0
      *out_ptr = 0;
111
0
      input_consumed -= 1;
112
0
   }
113
114
6
   return written;
115
6
}
116
117
6
size_t hex_decode(uint8_t output[], const char input[], size_t input_length, bool ignore_ws) {
118
6
   size_t consumed = 0;
119
6
   size_t written = hex_decode(output, input, input_length, consumed, ignore_ws);
120
121
6
   if(consumed != input_length) {
122
0
      throw Invalid_Argument("hex_decode: input did not have full bytes");
123
0
   }
124
125
6
   return written;
126
6
}
127
128
0
size_t hex_decode(uint8_t output[], std::string_view input, bool ignore_ws) {
129
0
   return hex_decode(output, input.data(), input.length(), ignore_ws);
130
0
}
131
132
0
size_t hex_decode(std::span<uint8_t> output, std::string_view input, bool ignore_ws) {
133
0
   return hex_decode(output.data(), input.data(), input.length(), ignore_ws);
134
0
}
135
136
6
secure_vector<uint8_t> hex_decode_locked(const char input[], size_t input_length, bool ignore_ws) {
137
6
   secure_vector<uint8_t> bin(1 + input_length / 2);
138
139
6
   size_t written = hex_decode(bin.data(), input, input_length, ignore_ws);
140
141
6
   bin.resize(written);
142
6
   return bin;
143
6
}
144
145
0
secure_vector<uint8_t> hex_decode_locked(std::string_view input, bool ignore_ws) {
146
0
   return hex_decode_locked(input.data(), input.size(), ignore_ws);
147
0
}
148
149
0
std::vector<uint8_t> hex_decode(const char input[], size_t input_length, bool ignore_ws) {
150
0
   std::vector<uint8_t> bin(1 + input_length / 2);
151
152
0
   size_t written = hex_decode(bin.data(), input, input_length, ignore_ws);
153
154
0
   bin.resize(written);
155
0
   return bin;
156
0
}
157
158
0
std::vector<uint8_t> hex_decode(std::string_view input, bool ignore_ws) {
159
0
   return hex_decode(input.data(), input.size(), ignore_ws);
160
0
}
161
162
}  // namespace Botan