Botan 3.13.0
Crypto and TLS for C&
hex.cpp
Go to the documentation of this file.
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/fmt.h>
14#include <botan/internal/int_utils.h>
15#include <botan/internal/loadstor.h>
16
17namespace Botan {
18
19namespace {
20
21uint16_t hex_encode_2nibble(uint8_t n8, bool uppercase) {
22 // Offset for upper or lower case 'a' resp
23 const uint16_t a_mask = uppercase ? 0x0707 : 0x2727;
24
25 const uint16_t n = (static_cast<uint16_t>(n8 & 0xF0) << 4) | (n8 & 0x0F);
26 // n >= 10? If so add offset
27 const uint16_t diff = swar_lt<uint16_t>(0x0909, n) & a_mask;
28 // Can't overflow between bytes, so don't need explicit SWAR addition:
29 return n + 0x3030 + diff;
30}
31
32} // namespace
33
34void hex_encode(char output[], const uint8_t input[], size_t input_length, bool uppercase) {
35 for(size_t i = 0; i != input_length; ++i) {
36 const uint16_t h = hex_encode_2nibble(input[i], uppercase);
37 output[2 * i] = get_byte<0>(h);
38 output[2 * i + 1] = get_byte<1>(h);
39 }
40}
41
42std::string hex_encode(const uint8_t input[], size_t input_length, bool uppercase) {
43 const size_t output_length = mul_or_throw<size_t>(2, input_length, "Input too large to hex encode");
44 std::string output(output_length, 0);
45
46 if(input_length > 0) {
47 hex_encode(&output.front(), input, input_length, uppercase);
48 }
49
50 return output;
51}
52
53namespace {
54
55uint8_t hex_char_to_bin(char input) {
56 // Starts of valid value ranges (v_lo) and their lengths (v_range)
57 constexpr uint64_t v_lo = make_uint64(0, '0', 'a', 'A', ' ', '\n', '\t', '\r');
58 constexpr uint64_t v_range = make_uint64(0, 10, 6, 6, 1, 1, 1, 1);
59 constexpr uint64_t expand8 = 0x0101010101010101;
60 constexpr uint64_t top64 = 0x8000000000000000;
61
62 const uint8_t x = static_cast<uint8_t>(input);
63 const uint64_t x8 = x * expand8;
64
65 const uint64_t v_mask = swar_in_range<uint64_t>(x8, v_lo, v_range) ^ top64;
66
67 // This is the offset added to x to get the value we need
68 const uint64_t val_v = 0xd0a9c960767773 ^ static_cast<uint64_t>(0xFF - x) << 56;
69
70 return x + static_cast<uint8_t>(val_v >> (8 * index_of_first_set_byte(v_mask)));
71}
72
73} // namespace
74
75size_t hex_decode(uint8_t output[], const char input[], size_t input_length, size_t& input_consumed, bool ignore_ws) {
76 uint8_t* out_ptr = output;
77 bool top_nibble = true;
78 uint8_t next = 0;
79
80 input_consumed = 0;
81
82 clear_mem(output, input_length / 2);
83
84 for(size_t i = 0; i != input_length; ++i) {
85 const uint8_t bin = hex_char_to_bin(input[i]);
86
87 if(bin >= 0x10) {
88 if(bin == 0x80 && ignore_ws) {
89 continue;
90 }
91
92 throw Invalid_Argument(fmt("hex_decode: invalid character '{}'", format_char_for_display(input[i])));
93 }
94
95 if(top_nibble) {
96 next = bin << 4;
97 } else {
98 next |= bin;
99 *out_ptr = next;
100 }
101
102 top_nibble = !top_nibble;
103 if(top_nibble) {
104 ++out_ptr;
105 input_consumed = i + 1;
106 }
107 }
108
109 /*
110 * Consume trailing whitespace following the last full byte; a leftover
111 * unpaired nibble (if any) stops the scan and is left unconsumed.
112 */
113 while(input_consumed < input_length && hex_char_to_bin(input[input_consumed]) == 0x80) {
114 ++input_consumed;
115 }
116
117 return (out_ptr - output);
118}
119
120size_t hex_decode(uint8_t output[], const char input[], size_t input_length, bool ignore_ws) {
121 size_t consumed = 0;
122 const size_t written = hex_decode(output, input, input_length, consumed, ignore_ws);
123
124 if(consumed != input_length) {
125 throw Invalid_Argument("hex_decode: input did not have full bytes");
126 }
127
128 return written;
129}
130
131size_t hex_decode(uint8_t output[], std::string_view input, bool ignore_ws) {
132 return hex_decode(output, input.data(), input.length(), ignore_ws);
133}
134
135size_t hex_decode(std::span<uint8_t> output, std::string_view input, bool ignore_ws) {
136 if(output.size() < input.length() / 2) {
137 throw Invalid_Argument("hex_decode: output buffer too small");
138 }
139 return hex_decode(output.data(), input.data(), input.length(), ignore_ws);
140}
141
142secure_vector<uint8_t> hex_decode_locked(const char input[], size_t input_length, bool ignore_ws) {
143 secure_vector<uint8_t> bin(1 + input_length / 2);
144
145 const size_t written = hex_decode(bin.data(), input, input_length, ignore_ws);
146
147 bin.resize(written);
148 return bin;
149}
150
151secure_vector<uint8_t> hex_decode_locked(std::string_view input, bool ignore_ws) {
152 return hex_decode_locked(input.data(), input.size(), ignore_ws);
153}
154
155std::vector<uint8_t> hex_decode(const char input[], size_t input_length, bool ignore_ws) {
156 std::vector<uint8_t> bin(1 + input_length / 2);
157
158 const size_t written = hex_decode(bin.data(), input, input_length, ignore_ws);
159
160 bin.resize(written);
161 return bin;
162}
163
164std::vector<uint8_t> hex_decode(std::string_view input, bool ignore_ws) {
165 return hex_decode(input.data(), input.size(), ignore_ws);
166}
167
168} // namespace Botan
constexpr uint8_t get_byte(T input)
Definition loadstor.h:79
std::string format_char_for_display(char c)
Definition charset.cpp:243
constexpr T mul_or_throw(T a, T b, std::string_view msg)
Definition int_utils.h:81
constexpr T swar_lt(T a, T b)
Definition int_utils.h:121
constexpr auto out_ptr(T &outptr) noexcept
Definition stl_util.h:148
constexpr uint64_t make_uint64(uint8_t i0, uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4, uint8_t i5, uint8_t i6, uint8_t i7)
Definition loadstor.h:121
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
constexpr T swar_in_range(T v, T lower, T upper)
Definition int_utils.h:144
secure_vector< uint8_t > hex_decode_locked(const char input[], size_t input_length, bool ignore_ws)
Definition hex.cpp:142
constexpr size_t index_of_first_set_byte(T v)
Definition int_utils.h:160
void hex_encode(char output[], const uint8_t input[], size_t input_length, bool uppercase)
Definition hex.cpp:34
size_t hex_decode(uint8_t output[], const char input[], size_t input_length, size_t &input_consumed, bool ignore_ws)
Definition hex.cpp:75
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:128
constexpr void clear_mem(T *ptr, size_t n)
Definition mem_ops.h:118