Azure C++ Utils 1.5.2+3
Azure REST API Helpers for Modern C++
C:/Users/maas/source/repos/siddiqsoft/azure-cpp-utils/src/base64-utils.hpp
Go to the documentation of this file.
1/*
2 azure-cpp-utils : Azure REST API Utilities for Modern C++
3
4 BSD 3-Clause License
5
6 Copyright (c) 2021, Siddiq Software LLC
7 All rights reserved.
8
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions are met:
11
12 1. Redistributions of source code must retain the above copyright notice, this
13 list of conditions and the following disclaimer.
14
15 2. Redistributions in binary form must reproduce the above copyright notice,
16 this list of conditions and the following disclaimer in the documentation
17 and/or other materials provided with the distribution.
18
19 3. Neither the name of the copyright holder nor the names of its
20 contributors may be used to endorse or promote products derived from
21 this software without specific prior written permission.
22
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
27 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 SERVICES; LOSS OF USE, d_, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35#pragma once
36
37#ifndef BASE64_UTILS_HPP
38#define BASE64_UTILS_HPP
39
40
41#include <iostream>
42#include <string>
43#include <concepts>
44#include <format>
45
46#if defined(_WIN64) || defined(WIN64) || defined(WIN32) || defined(_WIN32)
47#include <Windows.h>
48#include <wincrypt.h>
49#include <bcrypt.h>
50#pragma comment(lib, "bcrypt")
51#pragma comment(lib, "crypt32")
52#endif
53
54
56namespace siddiqsoft
57{
60 {
65 template <typename T = char>
66 requires std::same_as<T, char> || std::same_as<T, wchar_t>
67 static std::basic_string<T> urlEscape(const std::basic_string<T>& src)
68 {
69 if (!src.empty()) {
70 auto encodeVal {src};
71
72 // Make the value url-safe per https://tools.ietf.org/html/rfc4648#section-5
73 if constexpr (std::is_same_v<T, char>) {
74 std::ranges::for_each(encodeVal, [](T& ch) {
75 if (ch == '+')
76 ch = '-';
77 else if (ch == '/')
78 ch = '_';
79 });
80 encodeVal.erase(std::remove(encodeVal.begin(), encodeVal.end(), '\r'), encodeVal.end());
81 encodeVal.erase(std::remove(encodeVal.begin(), encodeVal.end(), '\n'), encodeVal.end());
82 encodeVal.erase(std::remove(encodeVal.begin(), encodeVal.end(), '='), encodeVal.end());
83
84 return encodeVal;
85 }
86 if constexpr (std::is_same_v<T, wchar_t>) {
87 std::ranges::for_each(encodeVal, [](T& ch) {
88 if (ch == L'+')
89 ch = L'-';
90 else if (ch == L'/')
91 ch = L'_';
92 });
93 encodeVal.erase(std::remove(encodeVal.begin(), encodeVal.end(), L'\r'), encodeVal.end());
94 encodeVal.erase(std::remove(encodeVal.begin(), encodeVal.end(), L'\n'), encodeVal.end());
95 encodeVal.erase(std::remove(encodeVal.begin(), encodeVal.end(), L'='), encodeVal.end());
96
97 return encodeVal;
98 }
99 }
100
101 return {};
102 }
103
104
110 template <typename T = char>
111 requires std::same_as<T, char> || std::same_as<T, wchar_t>
112 static std::basic_string<T> encode(const std::basic_string<T>& argBin)
113 {
114 DWORD destSize = 0;
115
116 /*
117 * NOTE: Cannot fold after the std::invoke() or std::apply() as the signature varies between the two functions.
118 * std::invoke( std::is_same_v<T,wchar_t> ? CryptBinaryToStringW : CryptBinaryToStringA, ... )
119 * fails to compile since the member types differ and it cannot cast the return into a single type.
120 */
121 if (!argBin.empty() &&
122 (TRUE == std::is_same_v<T, wchar_t> ? CryptBinaryToStringW((const BYTE*)(argBin.data()),
123 static_cast<DWORD>(argBin.length() * sizeof(wchar_t)),
124 CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
125 nullptr,
126 &destSize)
127 : CryptBinaryToStringA((const BYTE*)(argBin.data()),
128 static_cast<DWORD>(argBin.length() * sizeof(char)),
129 CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
130 nullptr,
131 &destSize)))
132 {
133 std::vector<T> dest(static_cast<size_t>(destSize) + 1);
134
135 if (TRUE == std::is_same_v<T, wchar_t> ? CryptBinaryToStringW((const BYTE*)(argBin.data()),
136 static_cast<DWORD>(argBin.length() * sizeof(wchar_t)),
137 CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
138 LPWSTR(dest.data()),
139 &destSize)
140 : CryptBinaryToStringA((const BYTE*)(argBin.data()),
141 static_cast<DWORD>(argBin.length() * sizeof(char)),
142 CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
143 LPSTR(dest.data()),
144 &destSize))
145 {
146 return std::basic_string<T> {reinterpret_cast<T*>(dest.data()), destSize};
147 }
148 }
149
150 // Fall-through is failure; return empty string
151 return {};
152 }
153
154
160 template <typename T = char>
161 requires std::same_as<T, char> || std::same_as<T, wchar_t>
162 static std::basic_string<T> decode(const std::basic_string<T>& textuallyEncoded)
163 {
164 DWORD destSize = 0;
165
166 if (textuallyEncoded.empty()) return {};
167
168 /*
169 * NOTE: Cannot fold after the std::invoke() or std::apply() as the signature varies between the two functions.
170 * std::invoke( std::is_same_v<T,wchar_t> ? CryptStringToBinaryW : CryptStringToBinaryA, ... )
171 * fails to compile since the member types differ and it cannot cast the return into a single type.
172 */
173 if (TRUE == std::is_same_v<wchar_t, T>
174 ? CryptStringToBinaryW(LPCWSTR(textuallyEncoded.c_str()),
175 static_cast<DWORD>(textuallyEncoded.length()), // in character count
176 CRYPT_STRING_BASE64,
177 nullptr, // output; NULL to calculate destSize
178 &destSize, // this is in bytes
179 nullptr,
180 nullptr)
181 : CryptStringToBinaryA(LPCSTR(textuallyEncoded.c_str()),
182 static_cast<DWORD>(textuallyEncoded.length()), // in character count
183 CRYPT_STRING_BASE64,
184 nullptr, // output; NULL to calculate destSize
185 &destSize, // this is in bytes
186 nullptr,
187 nullptr))
188 {
189 // The destSize is in bytes.. when we allocate, we must allocated for number of items
190 // For the case of wchar_t this would be to divide by sizeof(wchar_t)-->2.
191 // The +1 ensures we pad with blank/NUL item.
192 std::vector<T> dest(static_cast<size_t>(destSize / sizeof(T)) + 1);
193
194 if (TRUE == std::is_same_v<wchar_t, T> ? CryptStringToBinaryW(LPCWSTR(textuallyEncoded.c_str()),
195 static_cast<DWORD>(textuallyEncoded.length()),
196 CRYPT_STRING_BASE64,
197 reinterpret_cast<BYTE*>(dest.data()),
198 &destSize,
199 nullptr,
200 nullptr)
201 : CryptStringToBinaryA(LPCSTR(textuallyEncoded.c_str()),
202 static_cast<DWORD>(textuallyEncoded.length()),
203 CRYPT_STRING_BASE64,
204 reinterpret_cast<BYTE*>(dest.data()),
205 &destSize,
206 nullptr,
207 nullptr))
208 {
209 // The destSize is byte count and to get the character count we must divide by the sizeof(T) to obtain the
210 // correct character/string size.
211 return std::basic_string<T> {reinterpret_cast<T*>(dest.data()), destSize / sizeof(T)};
212 }
213 }
214 else {
215 throw std::runtime_error(std::format("{} - CryptStringToBinary returned:{}", __func__, GetLastError()));
216 }
217
218 // Fall-through is failure; return empty string
219 return {};
220 }
221 };
222} // namespace siddiqsoft
223
224#endif // !AZURECPPUTILS_HPP
SiddiqSoft.
Base64 encode/decode functions.
static std::basic_string< T > encode(const std::basic_string< T > &argBin)
Base64 encode a given "binary" string and optionally url escape.
static std::basic_string< T > urlEscape(const std::basic_string< T > &src)
URL escape the base64 encoded string.
static std::basic_string< T > decode(const std::basic_string< T > &textuallyEncoded)
Base64 decode the given encoded string back to the binary value.