1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/http
7  
// Official repository: https://github.com/cppalliance/http
8  
//
8  
//
9  

9  

10  
#include "base64.hpp"
10  
#include "base64.hpp"
11  

11  

12  
namespace boost {
12  
namespace boost {
13  
namespace http {
13  
namespace http {
14  
namespace bcrypt {
14  
namespace bcrypt {
15  
namespace detail {
15  
namespace detail {
16  

16  

17  
namespace {
17  
namespace {
18  

18  

19  
// bcrypt's non-standard base64 alphabet
19  
// bcrypt's non-standard base64 alphabet
20  
constexpr char encode_table[] =
20  
constexpr char encode_table[] =
21  
    "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
21  
    "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
22  

22  

23  
// Decode table: maps ASCII char to 6-bit value, or 0xFF for invalid
23  
// Decode table: maps ASCII char to 6-bit value, or 0xFF for invalid
24  
constexpr std::uint8_t decode_table[256] = {
24  
constexpr std::uint8_t decode_table[256] = {
25  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // 0-7
25  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // 0-7
26  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // 8-15
26  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // 8-15
27  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // 16-23
27  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // 16-23
28  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // 24-31
28  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // 24-31
29  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // 32-39
29  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // 32-39
30  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01,  // 40-47  (. /)
30  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01,  // 40-47  (. /)
31  
    0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D,  // 48-55  (0-7)
31  
    0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D,  // 48-55  (0-7)
32  
    0x3E, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // 56-63  (8-9)
32  
    0x3E, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // 56-63  (8-9)
33  
    0xFF, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,  // 64-71  (A-G)
33  
    0xFF, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,  // 64-71  (A-G)
34  
    0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,  // 72-79  (H-O)
34  
    0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,  // 72-79  (H-O)
35  
    0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,  // 80-87  (P-W)
35  
    0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,  // 80-87  (P-W)
36  
    0x19, 0x1A, 0x1B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // 88-95  (X-Z)
36  
    0x19, 0x1A, 0x1B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // 88-95  (X-Z)
37  
    0xFF, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22,  // 96-103 (a-g)
37  
    0xFF, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22,  // 96-103 (a-g)
38  
    0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,  // 104-111 (h-o)
38  
    0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,  // 104-111 (h-o)
39  
    0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32,  // 112-119 (p-w)
39  
    0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32,  // 112-119 (p-w)
40  
    0x33, 0x34, 0x35, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // 120-127 (x-z)
40  
    0x33, 0x34, 0x35, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  // 120-127 (x-z)
41  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
41  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
42  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
42  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
43  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
43  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
44  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
44  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
45  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
45  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
46  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
46  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
47  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
47  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
48  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
48  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
49  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
49  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
50  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
50  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
51  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
51  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
52  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
52  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
53  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
53  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
54  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
54  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
55  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
55  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
56  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
56  
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
57  
};
57  
};
58  

58  

59  
} // namespace
59  
} // namespace
60  

60  

61  
std::size_t
61  
std::size_t
62  
base64_encode(
62  
base64_encode(
63  
    char* dest,
63  
    char* dest,
64  
    std::uint8_t const* src,
64  
    std::uint8_t const* src,
65  
    std::size_t n)
65  
    std::size_t n)
66  
{
66  
{
67  
    char* out = dest;
67  
    char* out = dest;
68  

68  

69  
    while (n >= 3)
69  
    while (n >= 3)
70  
    {
70  
    {
71  
        std::uint32_t v =
71  
        std::uint32_t v =
72  
            (static_cast<std::uint32_t>(src[0]) << 16) |
72  
            (static_cast<std::uint32_t>(src[0]) << 16) |
73  
            (static_cast<std::uint32_t>(src[1]) << 8) |
73  
            (static_cast<std::uint32_t>(src[1]) << 8) |
74  
            static_cast<std::uint32_t>(src[2]);
74  
            static_cast<std::uint32_t>(src[2]);
75  

75  

76  
        *out++ = encode_table[(v >> 18) & 0x3F];
76  
        *out++ = encode_table[(v >> 18) & 0x3F];
77  
        *out++ = encode_table[(v >> 12) & 0x3F];
77  
        *out++ = encode_table[(v >> 12) & 0x3F];
78  
        *out++ = encode_table[(v >> 6) & 0x3F];
78  
        *out++ = encode_table[(v >> 6) & 0x3F];
79  
        *out++ = encode_table[v & 0x3F];
79  
        *out++ = encode_table[v & 0x3F];
80  

80  

81  
        src += 3;
81  
        src += 3;
82  
        n -= 3;
82  
        n -= 3;
83  
    }
83  
    }
84  

84  

85  
    if (n == 2)
85  
    if (n == 2)
86  
    {
86  
    {
87  
        std::uint32_t v =
87  
        std::uint32_t v =
88  
            (static_cast<std::uint32_t>(src[0]) << 16) |
88  
            (static_cast<std::uint32_t>(src[0]) << 16) |
89  
            (static_cast<std::uint32_t>(src[1]) << 8);
89  
            (static_cast<std::uint32_t>(src[1]) << 8);
90  

90  

91  
        *out++ = encode_table[(v >> 18) & 0x3F];
91  
        *out++ = encode_table[(v >> 18) & 0x3F];
92  
        *out++ = encode_table[(v >> 12) & 0x3F];
92  
        *out++ = encode_table[(v >> 12) & 0x3F];
93  
        *out++ = encode_table[(v >> 6) & 0x3F];
93  
        *out++ = encode_table[(v >> 6) & 0x3F];
94  
    }
94  
    }
95  
    else if (n == 1)
95  
    else if (n == 1)
96  
    {
96  
    {
97  
        std::uint32_t v =
97  
        std::uint32_t v =
98  
            static_cast<std::uint32_t>(src[0]) << 16;
98  
            static_cast<std::uint32_t>(src[0]) << 16;
99  

99  

100  
        *out++ = encode_table[(v >> 18) & 0x3F];
100  
        *out++ = encode_table[(v >> 18) & 0x3F];
101  
        *out++ = encode_table[(v >> 12) & 0x3F];
101  
        *out++ = encode_table[(v >> 12) & 0x3F];
102  
    }
102  
    }
103  

103  

104  
    return static_cast<std::size_t>(out - dest);
104  
    return static_cast<std::size_t>(out - dest);
105  
}
105  
}
106  

106  

107  
int
107  
int
108  
base64_decode(
108  
base64_decode(
109  
    std::uint8_t* dest,
109  
    std::uint8_t* dest,
110  
    char const* src,
110  
    char const* src,
111  
    std::size_t n)
111  
    std::size_t n)
112  
{
112  
{
113  
    std::uint8_t* out = dest;
113  
    std::uint8_t* out = dest;
114  
    std::size_t i = 0;
114  
    std::size_t i = 0;
115  

115  

116  
    while (i + 4 <= n)
116  
    while (i + 4 <= n)
117  
    {
117  
    {
118  
        std::uint8_t a = decode_table[static_cast<unsigned char>(src[i])];
118  
        std::uint8_t a = decode_table[static_cast<unsigned char>(src[i])];
119  
        std::uint8_t b = decode_table[static_cast<unsigned char>(src[i + 1])];
119  
        std::uint8_t b = decode_table[static_cast<unsigned char>(src[i + 1])];
120  
        std::uint8_t c = decode_table[static_cast<unsigned char>(src[i + 2])];
120  
        std::uint8_t c = decode_table[static_cast<unsigned char>(src[i + 2])];
121  
        std::uint8_t d = decode_table[static_cast<unsigned char>(src[i + 3])];
121  
        std::uint8_t d = decode_table[static_cast<unsigned char>(src[i + 3])];
122  

122  

123  
        if ((a | b | c | d) & 0x80)
123  
        if ((a | b | c | d) & 0x80)
124  
            return -1;
124  
            return -1;
125  

125  

126  
        std::uint32_t v =
126  
        std::uint32_t v =
127  
            (static_cast<std::uint32_t>(a) << 18) |
127  
            (static_cast<std::uint32_t>(a) << 18) |
128  
            (static_cast<std::uint32_t>(b) << 12) |
128  
            (static_cast<std::uint32_t>(b) << 12) |
129  
            (static_cast<std::uint32_t>(c) << 6) |
129  
            (static_cast<std::uint32_t>(c) << 6) |
130  
            static_cast<std::uint32_t>(d);
130  
            static_cast<std::uint32_t>(d);
131  

131  

132  
        *out++ = static_cast<std::uint8_t>(v >> 16);
132  
        *out++ = static_cast<std::uint8_t>(v >> 16);
133  
        *out++ = static_cast<std::uint8_t>(v >> 8);
133  
        *out++ = static_cast<std::uint8_t>(v >> 8);
134  
        *out++ = static_cast<std::uint8_t>(v);
134  
        *out++ = static_cast<std::uint8_t>(v);
135  

135  

136  
        i += 4;
136  
        i += 4;
137  
    }
137  
    }
138  

138  

139  
    // Handle remaining 2 or 3 characters
139  
    // Handle remaining 2 or 3 characters
140  
    if (i + 3 == n)
140  
    if (i + 3 == n)
141  
    {
141  
    {
142  
        std::uint8_t a = decode_table[static_cast<unsigned char>(src[i])];
142  
        std::uint8_t a = decode_table[static_cast<unsigned char>(src[i])];
143  
        std::uint8_t b = decode_table[static_cast<unsigned char>(src[i + 1])];
143  
        std::uint8_t b = decode_table[static_cast<unsigned char>(src[i + 1])];
144  
        std::uint8_t c = decode_table[static_cast<unsigned char>(src[i + 2])];
144  
        std::uint8_t c = decode_table[static_cast<unsigned char>(src[i + 2])];
145  

145  

146  
        if ((a | b | c) & 0x80)
146  
        if ((a | b | c) & 0x80)
147  
            return -1;
147  
            return -1;
148  

148  

149  
        std::uint32_t v =
149  
        std::uint32_t v =
150  
            (static_cast<std::uint32_t>(a) << 18) |
150  
            (static_cast<std::uint32_t>(a) << 18) |
151  
            (static_cast<std::uint32_t>(b) << 12) |
151  
            (static_cast<std::uint32_t>(b) << 12) |
152  
            (static_cast<std::uint32_t>(c) << 6);
152  
            (static_cast<std::uint32_t>(c) << 6);
153  

153  

154  
        *out++ = static_cast<std::uint8_t>(v >> 16);
154  
        *out++ = static_cast<std::uint8_t>(v >> 16);
155  
        *out++ = static_cast<std::uint8_t>(v >> 8);
155  
        *out++ = static_cast<std::uint8_t>(v >> 8);
156  
    }
156  
    }
157  
    else if (i + 2 == n)
157  
    else if (i + 2 == n)
158  
    {
158  
    {
159  
        std::uint8_t a = decode_table[static_cast<unsigned char>(src[i])];
159  
        std::uint8_t a = decode_table[static_cast<unsigned char>(src[i])];
160  
        std::uint8_t b = decode_table[static_cast<unsigned char>(src[i + 1])];
160  
        std::uint8_t b = decode_table[static_cast<unsigned char>(src[i + 1])];
161  

161  

162  
        if ((a | b) & 0x80)
162  
        if ((a | b) & 0x80)
163  
            return -1;
163  
            return -1;
164  

164  

165  
        std::uint32_t v =
165  
        std::uint32_t v =
166  
            (static_cast<std::uint32_t>(a) << 18) |
166  
            (static_cast<std::uint32_t>(a) << 18) |
167  
            (static_cast<std::uint32_t>(b) << 12);
167  
            (static_cast<std::uint32_t>(b) << 12);
168  

168  

169  
        *out++ = static_cast<std::uint8_t>(v >> 16);
169  
        *out++ = static_cast<std::uint8_t>(v >> 16);
170  
    }
170  
    }
171  
    else if (i + 1 == n)
171  
    else if (i + 1 == n)
172  
    {
172  
    {
173  
        // Single trailing character is invalid
173  
        // Single trailing character is invalid
174  
        return -1;
174  
        return -1;
175  
    }
175  
    }
176  

176  

177  
    return static_cast<int>(out - dest);
177  
    return static_cast<int>(out - dest);
178  
}
178  
}
179  

179  

180  
} // detail
180  
} // detail
181  
} // bcrypt
181  
} // bcrypt
182  
} // http
182  
} // http
183  
} // boost
183  
} // boost