C++ Utilities 5.30.0
Useful C++ classes and routines such as argument parser, IO and conversion utilities
Loading...
Searching...
No Matches
verification.h
Go to the documentation of this file.
1#ifndef CPP_UTILITIES_MISC_VERIFICATION_H
2#define CPP_UTILITIES_MISC_VERIFICATION_H
3
5
6#include <openssl/ec.h>
7#include <openssl/err.h>
8#include <openssl/evp.h>
9#include <openssl/pem.h>
10
11#include <algorithm>
12#include <array>
13#include <cctype>
14#include <string>
15#include <string_view>
16
17namespace CppUtilities {
18
19namespace Detail {
22inline std::string getOpenSslError()
23{
24 const auto errCode = ERR_get_error();
25 if (errCode == 0) {
26 return "unknown OpenSSL error";
27 }
28 auto buffer = std::array<char, 256>();
29 ERR_error_string_n(errCode, buffer.data(), buffer.size());
30 return std::string(buffer.data());
31}
32
35inline std::string extractPemBody(std::string_view pem, std::string_view header)
36{
37 auto body = std::string();
38 auto begin = pem.find(header);
39 if (begin == std::string_view::npos) {
40 return body;
41 }
42 begin += header.size();
43
44 auto end = pem.find("-----END", begin);
45 if (end == std::string_view::npos) {
46 return body;
47 }
48
49 body = std::string(pem.data() + begin, end - begin);
50 body.erase(std::remove_if(body.begin(), body.end(), ::isspace), body.end());
51 return body;
52}
53
56inline std::string parsePemSignature(std::string_view pemSignature, std::pair<std::unique_ptr<std::uint8_t[]>, std::uint32_t> &decodedSignature)
57{
58 const auto pemSignatureBody = extractPemBody(pemSignature, "-----BEGIN SIGNATURE-----");
59 if (pemSignatureBody.empty()) {
60 return "invalid or missing PEM signature block";
61 }
62 try {
63 decodedSignature = decodeBase64(pemSignatureBody.data(), static_cast<std::uint32_t>(pemSignatureBody.size()));
64 return std::string();
65 } catch (const ConversionException &e) {
66 return "unable to decode PEM signature block";
67 }
68}
69
70} // namespace Detail
71
73using MainVerifyFunctionType = std::string (*)(std::string_view, std::string_view, std::string_view);
74
107inline std::string verifySignature(std::string_view publicKeyPem, std::string_view signaturePem, std::string_view data)
108{
109 auto error = std::string();
110 auto derSignature = std::pair<std::unique_ptr<std::uint8_t[]>, std::uint32_t>();
111 if (error = Detail::parsePemSignature(signaturePem, derSignature); !error.empty()) {
112 return error;
113 }
114
115 BIO *const keyBio = BIO_new_mem_buf(publicKeyPem.data(), static_cast<int>(publicKeyPem.size()));
116 if (!keyBio) {
117 return error = "BIO_new_mem_buf failed: " + Detail::getOpenSslError();
118 }
119
120 EVP_PKEY *const publicKey = PEM_read_bio_PUBKEY(keyBio, nullptr, nullptr, nullptr);
121 BIO_free(keyBio);
122 if (!publicKey) {
123 return error = "PEM_read_bio_PUBKEY failed: " + Detail::getOpenSslError();
124 }
125
126 EVP_MD_CTX *const mdCtx = EVP_MD_CTX_new();
127 if (!mdCtx) {
128 EVP_PKEY_free(publicKey);
129 return error = "EVP_MD_CTX_new failed: " + Detail::getOpenSslError();
130 }
131
132 if (EVP_DigestVerifyInit(mdCtx, nullptr, EVP_sha256(), nullptr, publicKey) != 1) {
133 error = "EVP_DigestVerifyInit failed: " + Detail::getOpenSslError();
134 } else if (EVP_DigestVerifyUpdate(mdCtx, data.data(), data.size()) != 1) {
135 error = "EVP_DigestVerifyUpdate failed: " + Detail::getOpenSslError();
136 } else {
137 switch (EVP_DigestVerifyFinal(mdCtx, derSignature.first.get(), derSignature.second)) {
138 case 0:
139 error = "incorrect signature";
140 break;
141 case 1:
142 break; // signature is correct
143 default:
144 error = "EVP_DigestVerifyFinal failed: " + Detail::getOpenSslError();
145 break;
146 }
147 }
148
149 EVP_MD_CTX_free(mdCtx);
150 EVP_PKEY_free(publicKey);
151 return error;
152}
153
162template <class Keys, class VerifyFunction = MainVerifyFunctionType>
163inline std::string verifySignature(Keys &&publicKeysPem, std::string_view signaturePem, std::string_view data,
164 VerifyFunction &&verifyFunction = static_cast<MainVerifyFunctionType>(&verifySignature))
165{
166 auto error = std::string("no keys provided");
167 for (const auto publicKeyPem : publicKeysPem) {
168 if ((error = verifyFunction(publicKeyPem, signaturePem, data)).empty()) {
169 return error;
170 }
171 }
172 return error;
173}
174
175} // namespace CppUtilities
176
177#endif // CPP_UTILITIES_MISC_VERIFICATION_H
The ConversionException class is thrown by the various conversion functions of this library when a co...
std::string extractPemBody(std::string_view pem, std::string_view header)
Extracts the base64-encoded body from a PEM block.
std::string parsePemSignature(std::string_view pemSignature, std::pair< std::unique_ptr< std::uint8_t[]>, std::uint32_t > &decodedSignature)
Converts PEM-encoded signature into DER-encoded signature.
std::string getOpenSslError()
Returns the current OpenSSL error.
Contains all utilities provides by the c++utilities library.
std::string(*)(std::string_view, std::string_view, std::string_view) MainVerifyFunctionType
The signature of the main verifySignature() function.
std::string verifySignature(std::string_view publicKeyPem, std::string_view signaturePem, std::string_view data)
Verifies data with the specified public key publicKeyPem and signature signaturePem.
CPP_UTILITIES_EXPORT std::pair< std::unique_ptr< std::uint8_t[]>, std::uint32_t > decodeBase64(const char *encodedStr, const std::uint32_t strSize)
Decodes the specified Base64 encoded string.