CCF
Loading...
Searching...
No Matches
base64.h
Go to the documentation of this file.
1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the Apache 2.0 License.
3#pragma once
4
7
8#include <openssl/err.h>
9#include <openssl/evp.h>
10#include <string>
11#include <vector>
12
13// Inspired by openssl/test/evp_test.c
14// Ref: https://www.openssl.org/docs/man1.1.1/man3/EVP_DecodeBlock.html
15
16namespace ccf::crypto
17{
19 {
20 // Decode Base64 into byte stream
21 static std::vector<uint8_t> raw_from_b64(const std::string_view& b64_string)
22 {
23 const auto* const data =
24 reinterpret_cast<const uint8_t*>(b64_string.data());
25 const auto size = b64_string.size();
26
27 if (size == 0)
28 {
29 return {};
30 }
31
32 // Make sure the error queue is clean before we start
33 // Trying to ameliorate #3677 and #3368
34 ERR_clear_error();
35
36 // Initialise the encode context
38 EVP_DecodeInit(ctx);
39 int encoded_len = 0;
40
41 // Calculate the output buffer size: b64 is 6 bits per byte
42 int max_size = EVP_DECODE_LENGTH(size);
43 unsigned char output[max_size];
44 memset(output, '\0', max_size);
45
46 // Decode
47 int chunk_len = 0;
48 int rc = EVP_DecodeUpdate(ctx, output, &chunk_len, data, size);
49 if (rc < 0)
50 {
51 auto err_str = OpenSSL::error_string(ERR_get_error());
52 throw std::invalid_argument(fmt::format(
53 "OSSL: Could not decode update from base64 string: {} [{} bytes out "
54 "of {}, chunk_len = {}]",
55 err_str,
56 size,
57 max_size,
58 chunk_len));
59 }
60 encoded_len = chunk_len;
61
62 rc = EVP_DecodeFinal(ctx, output + chunk_len, &chunk_len);
63 if (rc != 1)
64 {
65 auto err_str = OpenSSL::error_string(ERR_get_error());
66 throw std::logic_error(fmt::format(
67 "OSSL: Could not decode final from base64 string: {} [{} bytes out "
68 "of {}, chunk_len = {}]",
69 err_str,
70 size,
71 max_size,
72 chunk_len));
73 }
74 encoded_len += chunk_len;
75
76 std::vector<uint8_t> ret(output, output + encoded_len);
77
78 return ret;
79 }
80
81 // Encode byte stream into Base64
82 static std::string b64_from_raw(const uint8_t* data, size_t size)
83 {
84 if (size == 0)
85 {
86 return "";
87 }
88
89 // Make sure the error queue is clean before we start
90 // Trying to ameliorate #3677 and #3368
91 ERR_clear_error();
92
93 // Initialise the encode context
95 EVP_EncodeInit(ctx);
96
97 // Calculate the output buffer size: b64 is 6 bits per byte
98 int max_size = EVP_ENCODE_LENGTH(size);
99 unsigned char output[max_size];
100 memset(output, '\0', max_size);
101
102 // Encode Main Block (if size > 48)
103 int chunk_len = 0;
104 int rc = EVP_EncodeUpdate(ctx, output, &chunk_len, data, size);
105 if (rc < 0)
106 {
107 auto err_str = OpenSSL::error_string(ERR_get_error());
108 throw std::logic_error(fmt::format(
109 "OSSL: Could not encode update to base64 string: {} [{} bytes out of "
110 "{}, chunk_len = {}]",
111 err_str,
112 size,
113 max_size,
114 chunk_len));
115 }
116
117 // Encode Final Line (after previous lines, if any)
118 EVP_EncodeFinal(ctx, output + chunk_len, &chunk_len);
119 auto err = ERR_get_error();
120 if (err != 0)
121 {
122 auto err_str = OpenSSL::error_string(err);
123 throw std::logic_error(fmt::format(
124 "OSSL: Could not encode final to base64 string: {} [{} bytes out of "
125 "{}, chunk_len = {}]",
126 err_str,
127 size,
128 max_size,
129 chunk_len));
130 }
131
132 // Clean up result (last \0, newlines)
133 std::string ret = reinterpret_cast<const char*>(output);
134 ret.pop_back();
135 ret.erase(std::remove(ret.begin(), ret.end(), '\n'), ret.end());
136
137 return ret;
138 }
139 };
140}
std::string error_string(unsigned long ec)
Returns the error string from an error code.
Definition openssl_wrappers.h:33
Definition base64.h:10
Definition base64.h:19
static std::vector< uint8_t > raw_from_b64(const std::string_view &b64_string)
Definition base64.h:21
static std::string b64_from_raw(const uint8_t *data, size_t size)
Definition base64.h:82
Definition openssl_wrappers.h:395