C++ Utilities 5.29.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 void initOpenSsl()
23{
24 ERR_load_crypto_strings();
25 OpenSSL_add_all_algorithms();
26}
27
30inline std::string getOpenSslError()
31{
32 const auto errCode = ERR_get_error();
33 if (errCode == 0) {
34 return "unknown OpenSSL error";
35 }
36 auto buffer = std::array<char, 256>();
37 ERR_error_string_n(errCode, buffer.data(), buffer.size());
38 return std::string(buffer.data());
39}
40
43inline std::string extractPemBody(std::string_view pem, std::string_view header)
44{
45 auto body = std::string();
46 auto begin = pem.find(header);
47 if (begin == std::string_view::npos) {
48 return body;
49 }
50 begin += header.size();
51
52 auto end = pem.find("-----END", begin);
53 if (end == std::string_view::npos) {
54 return body;
55 }
56
57 body = std::string(pem.data() + begin, end - begin);
58 body.erase(std::remove_if(body.begin(), body.end(), ::isspace), body.end());
59 return body;
60}
61
64inline std::string parsePemSignature(std::string_view pemSignature, std::pair<std::unique_ptr<std::uint8_t[]>, std::uint32_t> &decodedSignature)
65{
66 const auto pemSignatureBody = extractPemBody(pemSignature, "-----BEGIN SIGNATURE-----");
67 if (pemSignatureBody.empty()) {
68 return "invalid or missing PEM signature block";
69 }
70 try {
71 decodedSignature = decodeBase64(pemSignatureBody.data(), static_cast<std::uint32_t>(pemSignatureBody.size()));
72 return std::string();
73 } catch (const ConversionException &e) {
74 return "unable to decode PEM signature block";
75 }
76}
77
78} // namespace Detail
79
109inline std::string verifySignature(std::string_view publicKeyPem, std::string_view signaturePem, std::string_view data)
110{
111 auto error = std::string();
113
114 auto derSignature = std::pair<std::unique_ptr<std::uint8_t[]>, std::uint32_t>();
115 if (error = Detail::parsePemSignature(signaturePem, derSignature); !error.empty()) {
116 return error;
117 }
118
119 BIO *const keyBio = BIO_new_mem_buf(publicKeyPem.data(), static_cast<int>(publicKeyPem.size()));
120 if (!keyBio) {
121 return error = "BIO_new_mem_buf failed: " + Detail::getOpenSslError();
122 }
123
124 EVP_PKEY *const publicKey = PEM_read_bio_PUBKEY(keyBio, nullptr, nullptr, nullptr);
125 BIO_free(keyBio);
126 if (!publicKey) {
127 return error = "PEM_read_bio_PUBKEY failed: " + Detail::getOpenSslError();
128 }
129
130 EVP_MD_CTX *const mdCtx = EVP_MD_CTX_new();
131 if (!mdCtx) {
132 EVP_PKEY_free(publicKey);
133 return error = "EVP_MD_CTX_new failed: " + Detail::getOpenSslError();
134 }
135
136 if (EVP_DigestVerifyInit(mdCtx, nullptr, EVP_sha256(), nullptr, publicKey) != 1) {
137 error = "EVP_DigestVerifyInit failed: " + Detail::getOpenSslError();
138 } else if (EVP_DigestVerifyUpdate(mdCtx, data.data(), data.size()) != 1) {
139 error = "EVP_DigestVerifyUpdate failed: " + Detail::getOpenSslError();
140 } else {
141 switch (EVP_DigestVerifyFinal(mdCtx, derSignature.first.get(), derSignature.second)) {
142 case 0:
143 error = "incorrect signature";
144 break;
145 case 1:
146 break; // signature is correct
147 default:
148 error = "EVP_DigestVerifyFinal failed: " + Detail::getOpenSslError();
149 break;
150 }
151 }
152
153 EVP_MD_CTX_free(mdCtx);
154 EVP_PKEY_free(publicKey);
155 return error;
156}
157
158} // namespace CppUtilities
159
160#endif // CPP_UTILITIES_MISC_VERIFICATION_H
The ConversionException class is thrown by the various conversion functions of this library when a co...
void initOpenSsl()
Initializes OpenSSL.
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 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.