/src/rnp/src/lib/crypto/cipher_botan.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2021, [Ribose Inc](https://www.ribose.com). |
3 | | * All rights reserved. |
4 | | * |
5 | | * Redistribution and use in source and binary forms, with or without modification, |
6 | | * are permitted provided that the following conditions are met: |
7 | | * |
8 | | * 1. Redistributions of source code must retain the above copyright notice, |
9 | | * this list of conditions and the following disclaimer. |
10 | | * |
11 | | * 2. Redistributions in binary form must reproduce the above copyright notice, |
12 | | * this list of conditions and the following disclaimer in the documentation |
13 | | * and/or other materials provided with the distribution. |
14 | | * |
15 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
16 | | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
17 | | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
18 | | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE |
19 | | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 | | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
21 | | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
22 | | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
23 | | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
24 | | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 | | */ |
26 | | #include <sstream> |
27 | | #include <cassert> |
28 | | #include <botan/aead.h> |
29 | | #include "cipher_botan.hpp" |
30 | | #include "utils.h" |
31 | | #include "types.h" |
32 | | |
33 | | static const id_str_pair cipher_mode_map[] = { |
34 | | {PGP_CIPHER_MODE_CBC, "CBC"}, |
35 | | {PGP_CIPHER_MODE_OCB, "OCB"}, |
36 | | {0, NULL}, |
37 | | }; |
38 | | |
39 | | static const id_str_pair cipher_map[] = { |
40 | | {PGP_SA_AES_128, "AES-128"}, |
41 | | {PGP_SA_AES_256, "AES-256"}, |
42 | | {PGP_SA_IDEA, "IDEA"}, |
43 | | {0, NULL}, |
44 | | }; |
45 | | |
46 | | Cipher_Botan * |
47 | | Cipher_Botan::create(pgp_symm_alg_t alg, const std::string &name, bool encrypt) |
48 | 0 | { |
49 | | #if !defined(ENABLE_IDEA) |
50 | | if (alg == PGP_SA_IDEA) { |
51 | | RNP_LOG("IDEA support has been disabled"); |
52 | | return nullptr; |
53 | | } |
54 | | #endif |
55 | | #if !defined(ENABLE_BLOWFISH) |
56 | | if (alg == PGP_SA_BLOWFISH) { |
57 | | RNP_LOG("Blowfish support has been disabled"); |
58 | | return nullptr; |
59 | | } |
60 | | #endif |
61 | | #if !defined(ENABLE_CAST5) |
62 | | if (alg == PGP_SA_CAST5) { |
63 | | RNP_LOG("CAST5 support has been disabled"); |
64 | | return nullptr; |
65 | | } |
66 | | #endif |
67 | 0 | #if defined(CRYPTO_BACKEND_BOTAN3) |
68 | 0 | auto dir = encrypt ? Botan::Cipher_Dir::Encryption : Botan::Cipher_Dir::Decryption; |
69 | | #else |
70 | | auto dir = encrypt ? Botan::Cipher_Dir::ENCRYPTION : Botan::Cipher_Dir::DECRYPTION; |
71 | | #endif |
72 | 0 | auto cipher = Botan::Cipher_Mode::create(name, dir); |
73 | 0 | if (!cipher) { |
74 | 0 | RNP_LOG("Failed to create cipher '%s'", name.c_str()); |
75 | 0 | return nullptr; |
76 | 0 | } |
77 | 0 | return new (std::nothrow) Cipher_Botan(alg, std::move(cipher)); |
78 | 0 | } |
79 | | |
80 | | static std::string |
81 | | make_name(pgp_symm_alg_t cipher, pgp_cipher_mode_t mode, size_t tag_size, bool disable_padding) |
82 | 0 | { |
83 | 0 | const char *cipher_string = id_str_pair::lookup(cipher_map, cipher, NULL); |
84 | 0 | const char *mode_string = id_str_pair::lookup(cipher_mode_map, mode, NULL); |
85 | 0 | if (!cipher_string || !mode_string) { |
86 | 0 | return ""; |
87 | 0 | } |
88 | 0 | try { |
89 | 0 | std::stringstream ss; |
90 | 0 | ss << cipher_string << "/" << mode_string; |
91 | 0 | if (tag_size) { |
92 | 0 | ss << "(" << tag_size << ")"; |
93 | 0 | } |
94 | 0 | if (mode == PGP_CIPHER_MODE_CBC && disable_padding) { |
95 | 0 | ss << "/NoPadding"; |
96 | 0 | } |
97 | 0 | return ss.str(); |
98 | 0 | } catch (const std::exception &e) { |
99 | 0 | return ""; |
100 | 0 | } |
101 | 0 | } |
102 | | |
103 | | std::unique_ptr<Cipher_Botan> |
104 | | Cipher_Botan::encryption(pgp_symm_alg_t cipher, |
105 | | pgp_cipher_mode_t mode, |
106 | | size_t tag_size, |
107 | | bool disable_padding) |
108 | 0 | { |
109 | 0 | return std::unique_ptr<Cipher_Botan>( |
110 | 0 | create(cipher, make_name(cipher, mode, tag_size, disable_padding), true)); |
111 | 0 | } |
112 | | |
113 | | std::unique_ptr<Cipher_Botan> |
114 | | Cipher_Botan::decryption(pgp_symm_alg_t cipher, |
115 | | pgp_cipher_mode_t mode, |
116 | | size_t tag_size, |
117 | | bool disable_padding) |
118 | 0 | { |
119 | 0 | return std::unique_ptr<Cipher_Botan>( |
120 | 0 | create(cipher, make_name(cipher, mode, tag_size, disable_padding), false)); |
121 | 0 | } |
122 | | |
123 | | size_t |
124 | | Cipher_Botan::update_granularity() const |
125 | 0 | { |
126 | 0 | return m_cipher->update_granularity(); |
127 | 0 | } |
128 | | |
129 | | bool |
130 | | Cipher_Botan::set_key(const uint8_t *key, size_t key_length) |
131 | 0 | { |
132 | 0 | try { |
133 | 0 | m_cipher->set_key(key, key_length); |
134 | 0 | } catch (const std::exception &e) { |
135 | 0 | RNP_LOG("Failed to set key: %s", e.what()); |
136 | 0 | return false; |
137 | 0 | } |
138 | 0 | return true; |
139 | 0 | } |
140 | | |
141 | | bool |
142 | | Cipher_Botan::set_iv(const uint8_t *iv, size_t iv_length) |
143 | 0 | { |
144 | 0 | try { |
145 | 0 | m_cipher->start(iv, iv_length); |
146 | 0 | m_buf.reserve(this->update_granularity()); |
147 | 0 | } catch (const std::exception &e) { |
148 | 0 | RNP_LOG("Failed to set IV: %s", e.what()); |
149 | 0 | return false; |
150 | 0 | } |
151 | 0 | return true; |
152 | 0 | } |
153 | | |
154 | | bool |
155 | | Cipher_Botan::set_ad(const uint8_t *ad, size_t ad_length) |
156 | 0 | { |
157 | 0 | assert(m_cipher->authenticated()); |
158 | 0 | try { |
159 | 0 | dynamic_cast<Botan::AEAD_Mode &>(*m_cipher).set_associated_data(ad, ad_length); |
160 | 0 | } catch (const std::exception &e) { |
161 | 0 | RNP_LOG("Failed to set AAD: %s", e.what()); |
162 | 0 | return false; |
163 | 0 | } |
164 | 0 | return true; |
165 | 0 | } |
166 | | |
167 | | bool |
168 | | Cipher_Botan::update(uint8_t * output, |
169 | | size_t output_length, |
170 | | size_t * output_written, |
171 | | const uint8_t *input, |
172 | | size_t input_length, |
173 | | size_t * input_consumed) |
174 | 0 | { |
175 | 0 | try { |
176 | 0 | size_t ud = this->update_granularity(); |
177 | 0 | m_buf.resize(ud); |
178 | |
|
179 | 0 | *input_consumed = 0; |
180 | 0 | *output_written = 0; |
181 | 0 | while (input_length >= ud && output_length >= ud) { |
182 | 0 | m_buf.assign(input, input + ud); |
183 | 0 | size_t written = m_cipher->process(m_buf.data(), ud); |
184 | 0 | std::copy(m_buf.data(), m_buf.data() + written, output); |
185 | 0 | input += ud; |
186 | 0 | output += written; |
187 | 0 | input_length -= ud; |
188 | 0 | output_length -= written; |
189 | |
|
190 | 0 | *output_written += written; |
191 | 0 | *input_consumed += ud; |
192 | 0 | } |
193 | 0 | } catch (const std::exception &e) { |
194 | 0 | RNP_LOG("%s", e.what()); |
195 | 0 | return false; |
196 | 0 | } |
197 | 0 | return true; |
198 | 0 | } |
199 | | |
200 | | bool |
201 | | Cipher_Botan::finish(uint8_t * output, |
202 | | size_t output_length, |
203 | | size_t * output_written, |
204 | | const uint8_t *input, |
205 | | size_t input_length, |
206 | | size_t * input_consumed) |
207 | 0 | { |
208 | 0 | try { |
209 | 0 | *input_consumed = 0; |
210 | 0 | *output_written = 0; |
211 | 0 | size_t ud = this->update_granularity(); |
212 | 0 | if (input_length > ud) { |
213 | 0 | if (!update(output, |
214 | 0 | output_length, |
215 | 0 | output_written, |
216 | 0 | input, |
217 | 0 | input_length - ud, |
218 | 0 | input_consumed)) { |
219 | 0 | return false; |
220 | 0 | } |
221 | 0 | input += *input_consumed; |
222 | 0 | input_length = input_length - *input_consumed; |
223 | 0 | output += *output_written; |
224 | 0 | output_length -= *output_written; |
225 | 0 | } |
226 | 0 | Botan::secure_vector<uint8_t> final_block(input, input + input_length); |
227 | 0 | m_cipher->finish(final_block); |
228 | 0 | if (final_block.size() > output_length) { |
229 | 0 | RNP_LOG("Insufficient buffer"); |
230 | 0 | return false; |
231 | 0 | } |
232 | 0 | std::copy(final_block.begin(), final_block.end(), output); |
233 | 0 | *output_written += final_block.size(); |
234 | 0 | *input_consumed += input_length; |
235 | 0 | } catch (const std::exception &e) { |
236 | 0 | RNP_LOG("%s", e.what()); |
237 | 0 | return false; |
238 | 0 | } |
239 | 0 | return true; |
240 | 0 | } |
241 | | |
242 | | Cipher_Botan::Cipher_Botan(pgp_symm_alg_t alg, std::unique_ptr<Botan::Cipher_Mode> cipher) |
243 | 0 | : Cipher(alg), m_cipher(std::move(cipher)) |
244 | 0 | { |
245 | 0 | } |
246 | | |
247 | | Cipher_Botan::~Cipher_Botan() |
248 | 0 | { |
249 | 0 | } |