/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 |