Coverage Report

Created: 2025-11-09 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}