• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender / 1481376141

04 Oct 2024 07:02AM UTC coverage: 79.879% (-0.02%) from 79.897%
1481376141

Pull #1674

gitlab-ci

Mender
fix: explicitly initialize ssl

From the OpenSSL man pages:
Numerous internal OpenSSL functions call OPENSSL_init_ssl().
Therefore, in order to perform nondefault initialisation,
OPENSSL_init_ssl() MUST be called by application code prior to any other OpenSSL function calls.
See: https://docs.openssl.org/3.3/man3/OPENSSL_init_ssl/#description

This fixes errors where e.g. the openssl config configures ssl_conf,
which requires ssl to be initialized.

Ticket: MEN-7549
Changelog: Fix error while loading OpenSSL config file, by explicitly
initializing the SSL context prior to loading. Without the explicit
initialisation of SSL, the config might not be properly loaded if e.g.
it has sections specifying ssl settings. This was the case with the
example configuration for OpenSSL 1.1.1w from Debian Bullseye.

Signed-off-by: Daniel Skinstad Drabitzius <daniel.drabitzius@northern.tech>
(cherry picked from commit 4a3d82b83)
Pull Request #1674: [Cherry 4.0.x]: Explicitly initialize ssl

2 of 3 new or added lines in 1 file covered. (66.67%)

1 existing line in 1 file now uncovered.

7126 of 8921 relevant lines covered (79.88%)

12263.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.09
/src/common/crypto/platform/openssl/crypto.cpp
1
// Copyright 2023 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
#include <common/crypto.hpp>
16

17
#include <common/crypto/platform/openssl/openssl_config.h>
18

19
#include <cstdint>
20
#include <string>
21
#include <vector>
22
#include <memory>
23

24
#include <openssl/bn.h>
25
#include <openssl/ecdsa.h>
26
#include <openssl/err.h>
27
#include <openssl/engine.h>
28
#include <openssl/ui.h>
29
#include <openssl/ssl.h>
30
#ifndef MENDER_CRYPTO_OPENSSL_LEGACY
31
#include <openssl/provider.h>
32
#include <openssl/store.h>
33
#endif // MENDER_CRYPTO_OPENSSL_LEGACY
34

35
#include <openssl/evp.h>
36
#include <openssl/conf.h>
37
#include <openssl/pem.h>
38
#include <openssl/rsa.h>
39

40
#include <common/io.hpp>
41
#include <common/error.hpp>
42
#include <common/expected.hpp>
43
#include <common/common.hpp>
44

45
#include <artifact/sha/sha.hpp>
46

47

48
namespace mender {
49
namespace common {
50
namespace crypto {
51

52
const size_t MENDER_DIGEST_SHA256_LENGTH = 32;
53

54
const size_t OPENSSL_SUCCESS = 1;
55

56
using namespace std;
57

58
namespace error = mender::common::error;
59
namespace io = mender::common::io;
60

61
using EnginePtr = unique_ptr<ENGINE, void (*)(ENGINE *)>;
62
#ifndef MENDER_CRYPTO_OPENSSL_LEGACY
63
using ProviderPtr = unique_ptr<OSSL_PROVIDER, int (*)(OSSL_PROVIDER *)>;
64
#endif // MENDER_CRYPTO_OPENSSL_LEGACY
65

66
class OpenSSLResourceHandle {
×
67
public:
68
        EnginePtr engine;
69
};
70

71
auto resource_handle_free_func = [](OpenSSLResourceHandle *h) {
×
72
        if (h) {
×
73
                delete h;
×
74
        }
75
};
×
76

77
auto pkey_ctx_free_func = [](EVP_PKEY_CTX *ctx) {
28✔
78
        if (ctx) {
28✔
79
                EVP_PKEY_CTX_free(ctx);
28✔
80
        }
81
};
28✔
82
auto pkey_free_func = [](EVP_PKEY *key) {
54✔
83
        if (key) {
54✔
84
                EVP_PKEY_free(key);
54✔
85
        }
86
};
54✔
87
auto bio_free_func = [](BIO *bio) {
49✔
88
        if (bio) {
49✔
89
                BIO_free(bio);
49✔
90
        }
91
};
49✔
92
auto bio_free_all_func = [](BIO *bio) {
16✔
93
        if (bio) {
16✔
94
                BIO_free_all(bio);
16✔
95
        }
96
};
16✔
97
auto bn_free = [](BIGNUM *bn) {
×
98
        if (bn) {
×
99
                BN_free(bn);
×
100
        }
101
};
×
102
auto engine_free_func = [](ENGINE *e) {
×
103
        if (e) {
×
104
                ENGINE_free(e);
×
105
        }
106
};
×
107

108
auto password_callback = [](char *buf, int size, int rwflag, void *u) {
3✔
109
        // We'll only use this callback for reading passphrases, not for
110
        // writing them.
111
        assert(rwflag == 0);
112

113
        if (u == nullptr) {
3✔
114
                return 0;
115
        }
116

117
        // NB: buf is not expected to be null terminated.
118
        char *const pass = static_cast<char *>(u);
119
        strncpy(buf, pass, size);
3✔
120

121
        return static_cast<int>(strnlen(pass, size));
3✔
122
};
123

124

125
// NOTE: GetOpenSSLErrorMessage should be called upon all OpenSSL errors, as
126
// the errors are queued, and if not harvested, the FIFO structure of the
127
// queue will mean that if you just get one, you might actually get the wrong
128
// one.
129
string GetOpenSSLErrorMessage() {
18✔
130
        const auto sysErrorCode = errno;
18✔
131
        auto sslErrorCode = ERR_get_error();
18✔
132

133
        std::string errorDescription {};
134
        while (sslErrorCode != 0) {
57✔
135
                if (!errorDescription.empty()) {
39✔
136
                        errorDescription += '\n';
137
                }
138
                errorDescription += ERR_error_string(sslErrorCode, nullptr);
39✔
139
                sslErrorCode = ERR_get_error();
39✔
140
        }
141
        if (sysErrorCode != 0) {
18✔
142
                if (!errorDescription.empty()) {
16✔
143
                        errorDescription += '\n';
144
                }
145
                errorDescription += "System error, code=" + std::to_string(sysErrorCode);
32✔
146
                errorDescription += ", ";
16✔
147
                errorDescription += strerror(sysErrorCode);
16✔
148
        }
149
        return errorDescription;
18✔
150
}
151

152
ExpectedPrivateKey LoadFromHSMEngine(const Args &args) {
×
153
        log::Trace("Loading the private key from HSM");
×
154

155
        ENGINE_load_builtin_engines();
×
156
        auto engine = EnginePtr(ENGINE_by_id(args.ssl_engine.c_str()), engine_free_func);
×
157

158
        if (engine == nullptr) {
×
159
                return expected::unexpected(MakeError(
×
160
                        SetupError,
161
                        "Failed to get the " + args.ssl_engine
×
162
                                + " engine. No engine with the ID found: " + GetOpenSSLErrorMessage()));
×
163
        }
164
        log::Debug("Loaded the HSM engine successfully!");
×
165

166
        int res = ENGINE_init(engine.get());
×
167
        if (not res) {
×
168
                return expected::unexpected(MakeError(
×
169
                        SetupError,
170
                        "Failed to initialise the hardware security module (HSM): "
171
                                + GetOpenSSLErrorMessage()));
×
172
        }
173
        log::Debug("Successfully initialised the HSM engine");
×
174

175
        auto private_key = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
176
                ENGINE_load_private_key(
177
                        engine.get(),
178
                        args.private_key_path.c_str(),
179
                        (UI_METHOD *) nullptr,
180
                        nullptr /*callback_data */),
181
                pkey_free_func);
×
182
        if (private_key == nullptr) {
×
183
                return expected::unexpected(MakeError(
×
184
                        SetupError,
185
                        "Failed to load the private key from the hardware security module: "
186
                                + GetOpenSSLErrorMessage()));
×
187
        }
188
        log::Debug("Successfully loaded the private key from the HSM Engine: " + args.ssl_engine);
×
189

190
        auto handle = unique_ptr<OpenSSLResourceHandle, void (*)(OpenSSLResourceHandle *)>(
191
                new OpenSSLResourceHandle {std::move(engine)}, resource_handle_free_func);
×
192
        return PrivateKey(std::move(private_key), std::move(handle));
×
193
}
194

195
#ifdef MENDER_CRYPTO_OPENSSL_LEGACY
196
ExpectedPrivateKey LoadFrom(const Args &args) {
37✔
197
        log::Trace("Loading private key from file: " + args.private_key_path);
74✔
198
        auto private_bio_key = unique_ptr<BIO, void (*)(BIO *)>(
199
                BIO_new_file(args.private_key_path.c_str(), "r"), bio_free_func);
74✔
200
        if (private_bio_key == nullptr) {
37✔
201
                return expected::unexpected(MakeError(
6✔
202
                        SetupError,
203
                        "Failed to load the private key file " + args.private_key_path + ": "
12✔
204
                                + GetOpenSSLErrorMessage()));
30✔
205
        }
206

207
        char *passphrase = const_cast<char *>(args.private_key_passphrase.c_str());
208

209
        auto private_key = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
210
                PEM_read_bio_PrivateKey(private_bio_key.get(), nullptr, password_callback, passphrase),
211
                pkey_free_func);
62✔
212
        if (private_key == nullptr) {
31✔
213
                return expected::unexpected(MakeError(
4✔
214
                        SetupError,
215
                        "Failed to load the private key: " + args.private_key_path + " "
8✔
216
                                + GetOpenSSLErrorMessage()));
20✔
217
        }
218

219
        return PrivateKey(std::move(private_key));
27✔
220
}
221
#endif // MENDER_CRYPTO_OPENSSL_LEGACY
222

223
#ifndef MENDER_CRYPTO_OPENSSL_LEGACY
224
ExpectedPrivateKey LoadFrom(const Args &args) {
225
        char *passphrase = const_cast<char *>(args.private_key_passphrase.c_str());
226

227
        auto ui_method = unique_ptr<UI_METHOD, void (*)(UI_METHOD *)>(
228
                UI_UTIL_wrap_read_pem_callback(password_callback, 0 /* rw_flag */), UI_destroy_method);
229
        auto ctx = unique_ptr<OSSL_STORE_CTX, int (*)(OSSL_STORE_CTX *)>(
230
                OSSL_STORE_open(
231
                        args.private_key_path.c_str(),
232
                        ui_method.get(),
233
                        passphrase,
234
                        nullptr, /* OSSL_PARAM params[] */
235
                        nullptr),
236
                OSSL_STORE_close);
237

238
        if (ctx == nullptr) {
239
                return expected::unexpected(MakeError(
240
                        SetupError,
241
                        "Failed to load the private key from: " + args.private_key_path
242
                                + " error: " + GetOpenSSLErrorMessage()));
243
        }
244

245
        // Go through all objects in the context till we find the first private key
246
        while (not OSSL_STORE_eof(ctx.get())) {
247
                auto info = unique_ptr<OSSL_STORE_INFO, void (*)(OSSL_STORE_INFO *)>(
248
                        OSSL_STORE_load(ctx.get()), OSSL_STORE_INFO_free);
249

250
                if (info == nullptr) {
251
                        log::Error(
252
                                "Failed to load the the private key: " + args.private_key_path
253
                                + " trying the next object in the context: " + GetOpenSSLErrorMessage());
254
                        continue;
255
                }
256

257
                const int type_info {OSSL_STORE_INFO_get_type(info.get())};
258
                switch (type_info) {
259
                case OSSL_STORE_INFO_PKEY: {
260
                        // NOTE: get1 creates a duplicate of the pkey from the info, which can be
261
                        // used after the info ctx is destroyed
262
                        auto private_key = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
263
                                OSSL_STORE_INFO_get1_PKEY(info.get()), pkey_free_func);
264
                        if (private_key == nullptr) {
265
                                return expected::unexpected(MakeError(
266
                                        SetupError,
267
                                        "Failed to load the private key: " + args.private_key_path
268
                                                + " error: " + GetOpenSSLErrorMessage()));
269
                        }
270

271
                        return PrivateKey(std::move(private_key));
272
                }
273
                default:
274
                        const string info_type_string = OSSL_STORE_INFO_type_string(type_info);
275
                        log::Debug("Unhandled OpenSSL type: expected PrivateKey, got: " + info_type_string);
276
                        continue;
277
                }
278
        }
279

280
        return expected::unexpected(
281
                MakeError(SetupError, "Failed to load the private key: " + GetOpenSSLErrorMessage()));
282
}
283
#endif // ndef MENDER_CRYPTO_OPENSSL_LEGACY
284

285
ExpectedPrivateKey PrivateKey::Load(const Args &args) {
37✔
286
        // Numerous internal OpenSSL functions call OPENSSL_init_ssl().
287
        // Therefore, in order to perform nondefault initialisation,
288
        // OPENSSL_init_ssl() MUST be called by application code prior to any other OpenSSL function
289
        // calls. See: https://docs.openssl.org/3.3/man3/OPENSSL_init_ssl/#description
290
        if (OPENSSL_init_ssl(0, nullptr) != OPENSSL_SUCCESS) {
37✔
NEW
291
                log::Warning("Error initializing libssl: " + GetOpenSSLErrorMessage());
×
292
        }
293
        // Load OpenSSL config
294
        if (CONF_modules_load_file(nullptr, nullptr, 0) != OPENSSL_SUCCESS) {
37✔
UNCOV
295
                log::Warning("Failed to load OpenSSL configuration file: " + GetOpenSSLErrorMessage());
×
296
        }
297

298
        log::Trace("Loading private key");
74✔
299
        if (args.ssl_engine != "") {
37✔
300
                return LoadFromHSMEngine(args);
×
301
        }
302
        return LoadFrom(args);
37✔
303
}
304

305
ExpectedPrivateKey PrivateKey::Generate(const unsigned int bits, const unsigned int exponent) {
8✔
306
#ifdef MENDER_CRYPTO_OPENSSL_LEGACY
307
        auto pkey_gen_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
308
                EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr), pkey_ctx_free_func);
16✔
309

310
        int ret = EVP_PKEY_keygen_init(pkey_gen_ctx.get());
8✔
311
        if (ret != OPENSSL_SUCCESS) {
8✔
312
                return expected::unexpected(MakeError(
×
313
                        SetupError,
314
                        "Failed to generate a private key. Initialization failed: "
315
                                + GetOpenSSLErrorMessage()));
×
316
        }
317

318
        ret = EVP_PKEY_CTX_set_rsa_keygen_bits(pkey_gen_ctx.get(), bits);
8✔
319
        if (ret != OPENSSL_SUCCESS) {
8✔
320
                return expected::unexpected(MakeError(
×
321
                        SetupError,
322
                        "Failed to generate a private key. Parameters setting failed: "
323
                                + GetOpenSSLErrorMessage()));
×
324
        }
325

326
        auto exponent_bn = unique_ptr<BIGNUM, void (*)(BIGNUM *)>(BN_new(), bn_free);
16✔
327
        ret = BN_set_word(exponent_bn.get(), exponent);
8✔
328
        if (ret != OPENSSL_SUCCESS) {
8✔
329
                return expected::unexpected(MakeError(
×
330
                        SetupError,
331
                        "Failed to generate a private key. Parameters setting failed: "
332
                                + GetOpenSSLErrorMessage()));
×
333
        }
334

335
        ret = EVP_PKEY_CTX_set_rsa_keygen_pubexp(pkey_gen_ctx.get(), exponent_bn.get());
8✔
336
        if (ret != OPENSSL_SUCCESS) {
8✔
337
                return expected::unexpected(MakeError(
×
338
                        SetupError,
339
                        "Failed to generate a private key. Parameters setting failed: "
340
                                + GetOpenSSLErrorMessage()));
×
341
        }
342
        exponent_bn.release();
343

344
        EVP_PKEY *pkey = nullptr;
8✔
345
        ret = EVP_PKEY_keygen(pkey_gen_ctx.get(), &pkey);
8✔
346
        if (ret != OPENSSL_SUCCESS) {
8✔
347
                return expected::unexpected(MakeError(
×
348
                        SetupError,
349
                        "Failed to generate a private key. Generation failed: " + GetOpenSSLErrorMessage()));
×
350
        }
351
#else
352
        auto pkey_gen_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
353
                EVP_PKEY_CTX_new_from_name(nullptr, "RSA", nullptr), pkey_ctx_free_func);
354

355
        int ret = EVP_PKEY_keygen_init(pkey_gen_ctx.get());
356
        if (ret != OPENSSL_SUCCESS) {
357
                return expected::unexpected(MakeError(
358
                        SetupError,
359
                        "Failed to generate a private key. Initialization failed: "
360
                                + GetOpenSSLErrorMessage()));
361
        }
362

363
        OSSL_PARAM params[3];
364
        auto bits_buffer = bits;
365
        auto exponent_buffer = exponent;
366
        params[0] = OSSL_PARAM_construct_uint("bits", &bits_buffer);
367
        params[1] = OSSL_PARAM_construct_uint("e", &exponent_buffer);
368
        params[2] = OSSL_PARAM_construct_end();
369

370
        ret = EVP_PKEY_CTX_set_params(pkey_gen_ctx.get(), params);
371
        if (ret != OPENSSL_SUCCESS) {
372
                return expected::unexpected(MakeError(
373
                        SetupError,
374
                        "Failed to generate a private key. Parameters setting failed: "
375
                                + GetOpenSSLErrorMessage()));
376
        }
377

378
        EVP_PKEY *pkey = nullptr;
379
        ret = EVP_PKEY_generate(pkey_gen_ctx.get(), &pkey);
380
        if (ret != OPENSSL_SUCCESS) {
381
                return expected::unexpected(MakeError(
382
                        SetupError,
383
                        "Failed to generate a private key. Generation failed: " + GetOpenSSLErrorMessage()));
384
        }
385
#endif
386

387
        auto private_key = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(pkey, pkey_free_func);
8✔
388
        return PrivateKey(std::move(private_key));
8✔
389
}
390

391
expected::ExpectedString EncodeBase64(vector<uint8_t> to_encode) {
14✔
392
        // Predict the len of the decoded for later verification. From man page:
393
        // For every 3 bytes of input provided 4 bytes of output
394
        // data will be produced. If n is not divisible by 3 (...)
395
        // the output is padded such that it is always divisible by 4.
396
        const uint64_t predicted_len {4 * ((to_encode.size() + 2) / 3)};
14✔
397

398
        // Add space for a NUL terminator. From man page:
399
        // Additionally a NUL terminator character will be added
400
        auto buffer {vector<unsigned char>(predicted_len + 1)};
14✔
401

402
        const int64_t output_len {
403
                EVP_EncodeBlock(buffer.data(), to_encode.data(), static_cast<int>(to_encode.size()))};
14✔
404
        assert(output_len >= 0);
405

406
        if (predicted_len != static_cast<uint64_t>(output_len)) {
14✔
407
                return expected::unexpected(
×
408
                        MakeError(Base64Error, "The predicted and the actual length differ"));
×
409
        }
410

411
        return string(buffer.begin(), buffer.end() - 1); // Remove the last zero byte
28✔
412
}
413

414
expected::ExpectedBytes DecodeBase64(string to_decode) {
15✔
415
        // Predict the len of the decoded for later verification. From man page:
416
        // For every 4 input bytes exactly 3 output bytes will be
417
        // produced. The output will be padded with 0 bits if necessary
418
        // to ensure that the output is always 3 bytes.
419
        const uint64_t predicted_len {3 * ((to_decode.size() + 3) / 4)};
15✔
420

421
        auto buffer {vector<unsigned char>(predicted_len)};
15✔
422

423
        const int64_t output_len {EVP_DecodeBlock(
15✔
424
                buffer.data(),
425
                common::ByteVectorFromString(to_decode).data(),
15✔
426
                static_cast<int>(to_decode.size()))};
15✔
427
        assert(output_len >= 0);
428

429
        if (predicted_len != static_cast<uint64_t>(output_len)) {
15✔
430
                return expected::unexpected(MakeError(
×
431
                        Base64Error,
432
                        "The predicted (" + std::to_string(predicted_len) + ") and the actual ("
×
433
                                + std::to_string(output_len) + ") length differ"));
×
434
        }
435

436
        // Subtract padding bytes. Inspired by internal OpenSSL code from:
437
        // https://github.com/openssl/openssl/blob/ff88545e02ab48a52952350c52013cf765455dd3/crypto/ct/ct_b64.c#L46
438
        for (auto it = to_decode.crbegin(); *it == '='; it++) {
25✔
439
                buffer.pop_back();
440
        }
441

442
        return buffer;
15✔
443
}
444

445

446
expected::ExpectedString ExtractPublicKey(const Args &args) {
9✔
447
        auto exp_private_key = PrivateKey::Load(args);
9✔
448
        if (!exp_private_key) {
9✔
449
                return expected::unexpected(exp_private_key.error());
2✔
450
        }
451

452
        auto bio_public_key = unique_ptr<BIO, void (*)(BIO *)>(BIO_new(BIO_s_mem()), bio_free_all_func);
16✔
453

454
        if (!bio_public_key.get()) {
8✔
455
                return expected::unexpected(MakeError(
×
456
                        SetupError,
457
                        "Failed to extract the public key from the private key " + args.private_key_path
×
458
                                + "):" + GetOpenSSLErrorMessage()));
×
459
        }
460

461
        int ret = PEM_write_bio_PUBKEY(bio_public_key.get(), exp_private_key.value().Get());
8✔
462
        if (ret != OPENSSL_SUCCESS) {
8✔
463
                return expected::unexpected(MakeError(
×
464
                        SetupError,
465
                        "Failed to extract the public key from the private key (" + args.private_key_path
×
466
                                + "): OpenSSL BIO write failed: " + GetOpenSSLErrorMessage()));
×
467
        }
468

469
        // NOTE: At this point we already have a public key available for extraction.
470
        // However, when using some providers in OpenSSL3 the external provider might
471
        // write the key in the old PKCS#1 format. The format is not deprecated, but
472
        // our older backends only understand the format if it is in the PKCS#8
473
        // (SubjectPublicKey) format:
474
        //
475
        // For us who don't speak OpenSSL:
476
        //
477
        // -- BEGIN RSA PUBLIC KEY -- <- PKCS#1 (old format)
478
        // -- BEGIN PUBLIC KEY -- <- PKCS#8 (new format - can hold different key types)
479

480

481
        auto evp_public_key = PkeyPtr(
482
                PEM_read_bio_PUBKEY(bio_public_key.get(), nullptr, nullptr, nullptr), pkey_free_func);
16✔
483

484
        if (evp_public_key == nullptr) {
8✔
485
                return expected::unexpected(MakeError(
×
486
                        SetupError,
487
                        "Failed to extract the public key from the private key " + args.private_key_path
×
488
                                + "):" + GetOpenSSLErrorMessage()));
×
489
        }
490

491
        auto bio_public_key_new =
492
                unique_ptr<BIO, void (*)(BIO *)>(BIO_new(BIO_s_mem()), bio_free_all_func);
16✔
493

494
        if (bio_public_key_new == nullptr) {
8✔
495
                return expected::unexpected(MakeError(
×
496
                        SetupError,
497
                        "Failed to extract the public key from the public key " + args.private_key_path
×
498
                                + "):" + GetOpenSSLErrorMessage()));
×
499
        }
500

501
        ret = PEM_write_bio_PUBKEY(bio_public_key_new.get(), evp_public_key.get());
8✔
502
        if (ret != OPENSSL_SUCCESS) {
8✔
503
                return expected::unexpected(MakeError(
×
504
                        SetupError,
505
                        "Failed to extract the public key from the private key: (" + args.private_key_path
×
506
                                + "): OpenSSL BIO write failed: " + GetOpenSSLErrorMessage()));
×
507
        }
508

509
        int pending = BIO_ctrl_pending(bio_public_key_new.get());
8✔
510
        if (pending <= 0) {
8✔
511
                return expected::unexpected(MakeError(
×
512
                        SetupError,
513
                        "Failed to extract the public key from bio ctrl: (" + args.private_key_path
×
514
                                + "): Zero byte key unexpected: " + GetOpenSSLErrorMessage()));
×
515
        }
516

517
        vector<uint8_t> key_vector(pending);
8✔
518

519
        size_t read = BIO_read(bio_public_key_new.get(), key_vector.data(), pending);
8✔
520

521
        if (read == 0) {
8✔
522
                MakeError(
×
523
                        SetupError,
524
                        "Failed to extract the public key from (" + args.private_key_path
×
525
                                + "): Zero bytes read from BIO: " + GetOpenSSLErrorMessage());
×
526
        }
527

528
        return string(key_vector.begin(), key_vector.end());
16✔
529
}
530

531
static expected::ExpectedBytes SignED25519(EVP_PKEY *pkey, const vector<uint8_t> &raw_data) {
1✔
532
        size_t sig_len;
533

534
        auto md_ctx = unique_ptr<EVP_MD_CTX, void (*)(EVP_MD_CTX *)>(EVP_MD_CTX_new(), EVP_MD_CTX_free);
2✔
535
        if (md_ctx == nullptr) {
1✔
536
                return expected::unexpected(MakeError(
×
537
                        SetupError, "Failed to initialize the OpenSSL md_ctx: " + GetOpenSSLErrorMessage()));
×
538
        }
539

540
        int ret {EVP_DigestSignInit(md_ctx.get(), nullptr, nullptr, nullptr, pkey)};
1✔
541
        if (ret != OPENSSL_SUCCESS) {
1✔
542
                return expected::unexpected(MakeError(
×
543
                        SetupError, "Failed to initialize the OpenSSL signature: " + GetOpenSSLErrorMessage()));
×
544
        }
545

546
        /* Calculate the required size for the signature by passing a nullptr buffer */
547
        ret = EVP_DigestSign(md_ctx.get(), nullptr, &sig_len, raw_data.data(), raw_data.size());
1✔
548
        if (ret != OPENSSL_SUCCESS) {
1✔
549
                return expected::unexpected(MakeError(
×
550
                        SetupError,
551
                        "Failed to find the required size of the signature buffer: "
552
                                + GetOpenSSLErrorMessage()));
×
553
        }
554

555
        vector<uint8_t> sig(sig_len);
1✔
556
        ret = EVP_DigestSign(md_ctx.get(), sig.data(), &sig_len, raw_data.data(), raw_data.size());
1✔
557
        if (ret != OPENSSL_SUCCESS) {
1✔
558
                return expected::unexpected(
×
559
                        MakeError(SetupError, "Failed to sign the message: " + GetOpenSSLErrorMessage()));
×
560
        }
561

562
        // The signature may in some cases be shorter than the previously allocated
563
        // length (which is the max)
564
        sig.resize(sig_len);
1✔
565

566
        return sig;
1✔
567
}
568

569
expected::ExpectedBytes SignGeneric(PrivateKey &private_key, const vector<uint8_t> &digest) {
9✔
570
        auto pkey_signer_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
571
                EVP_PKEY_CTX_new(private_key.Get(), nullptr), pkey_ctx_free_func);
18✔
572

573
        if (EVP_PKEY_sign_init(pkey_signer_ctx.get()) <= 0) {
9✔
574
                return expected::unexpected(MakeError(
×
575
                        SetupError, "Failed to initialize the OpenSSL signer: " + GetOpenSSLErrorMessage()));
×
576
        }
577
        if (EVP_PKEY_CTX_set_signature_md(pkey_signer_ctx.get(), EVP_sha256()) <= 0) {
9✔
578
                return expected::unexpected(MakeError(
×
579
                        SetupError,
580
                        "Failed to set the OpenSSL signature to sha256: " + GetOpenSSLErrorMessage()));
×
581
        }
582

583
        vector<uint8_t> signature {};
584

585
        // Set the needed signature buffer length
586
        size_t digestlength = MENDER_DIGEST_SHA256_LENGTH, siglength;
587
        if (EVP_PKEY_sign(pkey_signer_ctx.get(), nullptr, &siglength, digest.data(), digestlength)
9✔
588
                <= 0) {
589
                return expected::unexpected(MakeError(
×
590
                        SetupError, "Failed to get the signature buffer length: " + GetOpenSSLErrorMessage()));
×
591
        }
592
        signature.resize(siglength);
9✔
593

594
        if (EVP_PKEY_sign(
9✔
595
                        pkey_signer_ctx.get(), signature.data(), &siglength, digest.data(), digestlength)
596
                <= 0) {
597
                return expected::unexpected(
×
598
                        MakeError(SetupError, "Failed to sign the digest: " + GetOpenSSLErrorMessage()));
×
599
        }
600

601
        // The signature may in some cases be shorter than the previously allocated
602
        // length (which is the max)
603
        signature.resize(siglength);
9✔
604

605
        return signature;
9✔
606
}
607

608
expected::ExpectedBytes SignData(const Args &args, const vector<uint8_t> &raw_data) {
11✔
609
        auto exp_private_key = PrivateKey::Load(args);
11✔
610
        if (!exp_private_key) {
11✔
611
                return expected::unexpected(exp_private_key.error());
2✔
612
        }
613

614
        log::Info("Signing with: " + args.private_key_path);
10✔
615

616
        auto key_type = EVP_PKEY_base_id(exp_private_key.value().Get());
10✔
617

618
        // ED25519 signatures need to be handled independently, because of how the
619
        // signature scheme is designed.
620
        if (key_type == EVP_PKEY_ED25519) {
10✔
621
                return SignED25519(exp_private_key.value().Get(), raw_data);
1✔
622
        }
623

624
        auto exp_shasum = mender::sha::Shasum(raw_data);
9✔
625
        if (!exp_shasum) {
9✔
626
                return expected::unexpected(exp_shasum.error());
×
627
        }
628
        auto digest = exp_shasum.value(); /* The shasummed data = digest in crypto world */
9✔
629
        log::Debug("Shasum is: " + digest.String());
18✔
630

631
        return SignGeneric(exp_private_key.value(), digest);
18✔
632
}
633

634
expected::ExpectedString Sign(const Args &args, const vector<uint8_t> &raw_data) {
11✔
635
        auto exp_signed_data = SignData(args, raw_data);
11✔
636
        if (!exp_signed_data) {
11✔
637
                return expected::unexpected(exp_signed_data.error());
2✔
638
        }
639
        vector<uint8_t> signature = exp_signed_data.value();
10✔
640

641
        return EncodeBase64(signature);
20✔
642
}
643

644
const size_t mender_decode_buf_size = 256;
645
const size_t ecdsa256keySize = 32;
646

647
// Try and decode the keys from pure binary, assuming that the points on the
648
// curve (r,s), have been concatenated together (r || s), and simply dumped to
649
// binary. Which is what we did in the `mender-artifact` tool.
650
// (See MEN-1740) for some insight into previous issues, and the chosen fix.
651
static expected::ExpectedBytes TryASN1EncodeMenderCustomBinaryECFormat(
2✔
652
        const vector<uint8_t> &signature,
653
        const mender::sha::SHA &shasum,
654
        std::function<BIGNUM *(const unsigned char *signature, int length, BIGNUM *_unused)>
655
                BinaryDecoderFn) {
656
        // Verify that the marshalled keys match our expectation
657
        const size_t assumed_signature_size {2 * ecdsa256keySize};
658
        if (signature.size() > assumed_signature_size) {
2✔
659
                return expected::unexpected(MakeError(
×
660
                        SetupError,
661
                        "Unexpected size of the signature for ECDSA. Expected 2*" + to_string(ecdsa256keySize)
×
662
                                + " bytes. Got: " + to_string(signature.size())));
×
663
        }
664
        auto ecSig = unique_ptr<ECDSA_SIG, void (*)(ECDSA_SIG *)>(ECDSA_SIG_new(), ECDSA_SIG_free);
4✔
665
        if (ecSig == nullptr) {
2✔
666
                return expected::unexpected(MakeError(
×
667
                        SetupError,
668
                        "Failed to allocate the structure for the ECDSA signature: "
669
                                + GetOpenSSLErrorMessage()));
×
670
        }
671

672
        auto r = unique_ptr<BIGNUM, void (*)(BIGNUM *)>(
673
                BinaryDecoderFn(signature.data(), ecdsa256keySize, nullptr /* allocate new memory for r */),
674
                bn_free);
4✔
675
        if (r == nullptr) {
2✔
676
                return expected::unexpected(MakeError(
×
677
                        SetupError,
678
                        "Failed to extract the r(andom) part from the ECDSA signature in the binary representation: "
679
                                + GetOpenSSLErrorMessage()));
×
680
        }
681
        auto s = unique_ptr<BIGNUM, void (*)(BIGNUM *)>(
682
                BinaryDecoderFn(
683
                        signature.data() + ecdsa256keySize,
684
                        ecdsa256keySize,
685
                        nullptr /* allocate new memory for s */),
686
                bn_free);
4✔
687
        if (s == nullptr) {
2✔
688
                return expected::unexpected(MakeError(
×
689
                        SetupError,
690
                        "Failed to extract the s(ignature) part from the ECDSA signature in the binary representation: "
691
                                + GetOpenSSLErrorMessage()));
×
692
        }
693

694
        // Set the r&s values in the SIG struct
695
        // r & s now owned by ecSig
696
        int ret {ECDSA_SIG_set0(ecSig.get(), r.get(), s.get())};
2✔
697
        if (ret != OPENSSL_SUCCESS) {
2✔
698
                return expected::unexpected(MakeError(
×
699
                        SetupError,
700
                        "Failed to set the signature parts in the ECDSA structure: "
701
                                + GetOpenSSLErrorMessage()));
×
702
        }
703
        r.release();
704
        s.release();
705

706
        /* Allocate some array guaranteed to hold the DER-encoded structure */
707
        vector<uint8_t> der_encoded_byte_array(mender_decode_buf_size);
2✔
708
        unsigned char *arr_p = &der_encoded_byte_array[0];
2✔
709
        int len = i2d_ECDSA_SIG(ecSig.get(), &arr_p);
2✔
710
        if (len < 0) {
2✔
711
                return expected::unexpected(MakeError(
×
712
                        SetupError,
713
                        "Failed to set the signature parts in the ECDSA structure: "
714
                                + GetOpenSSLErrorMessage()));
×
715
        }
716
        /* Resize to the actual size of the DER-encoded signature */
717
        der_encoded_byte_array.resize(len);
2✔
718

719
        return der_encoded_byte_array;
2✔
720
}
721

722

723
expected::ExpectedBool VerifySignData(
724
        const string &public_key_path,
725
        const mender::sha::SHA &shasum,
726
        const vector<uint8_t> &signature);
727

728
static expected::ExpectedBool VerifyECDSASignData(
2✔
729
        const string &public_key_path,
730
        const mender::sha::SHA &shasum,
731
        const vector<uint8_t> &signature) {
732
        expected::ExpectedBytes exp_der_encoded_signature =
733
                TryASN1EncodeMenderCustomBinaryECFormat(signature, shasum, BN_bin2bn)
4✔
734
                        .or_else([&signature, &shasum](error::Error big_endian_error) {
×
735
                                log::Debug(
×
736
                                        "Failed to decode the signature binary blob from our custom binary format assuming the big-endian encoding, error: "
737
                                        + big_endian_error.String()
×
738
                                        + " falling back and trying anew assuming it is little-endian encoded: ");
×
739
                                return TryASN1EncodeMenderCustomBinaryECFormat(signature, shasum, BN_lebin2bn);
×
740
                        });
2✔
741
        if (!exp_der_encoded_signature) {
2✔
742
                return expected::unexpected(
×
743
                        MakeError(VerificationError, exp_der_encoded_signature.error().message));
×
744
        }
745

746
        vector<uint8_t> der_encoded_signature = exp_der_encoded_signature.value();
2✔
747

748
        return VerifySignData(public_key_path, shasum, der_encoded_signature);
2✔
749
}
750

751
static bool OpenSSLSignatureVerificationError(int a) {
752
        /*
753
         * The signature check errored. This is different from the signature being
754
         * wrong. We simply were not able to perform the check in this instance.
755
         * Therefore, we fall back to trying the custom marshalled binary ECDSA
756
         * signature, which we have been using in Mender.
757
         */
758
        return a < 0;
759
}
760

761
expected::ExpectedBool VerifySignData(
16✔
762
        const string &public_key_path,
763
        const mender::sha::SHA &shasum,
764
        const vector<uint8_t> &signature) {
765
        auto bio_key =
766
                unique_ptr<BIO, void (*)(BIO *)>(BIO_new_file(public_key_path.c_str(), "r"), bio_free_func);
32✔
767
        if (bio_key == nullptr) {
16✔
768
                return expected::unexpected(MakeError(
3✔
769
                        SetupError,
770
                        "Failed to open the public key file from (" + public_key_path
6✔
771
                                + "):" + GetOpenSSLErrorMessage()));
15✔
772
        }
773

774
        auto pkey = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
775
                PEM_read_bio_PUBKEY(bio_key.get(), nullptr, nullptr, nullptr), pkey_free_func);
26✔
776
        if (pkey == nullptr) {
13✔
777
                return expected::unexpected(MakeError(
2✔
778
                        SetupError,
779
                        "Failed to load the public key from(" + public_key_path
4✔
780
                                + "): " + GetOpenSSLErrorMessage()));
10✔
781
        }
782

783
        auto pkey_signer_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
784
                EVP_PKEY_CTX_new(pkey.get(), nullptr), pkey_ctx_free_func);
22✔
785

786
        auto ret = EVP_PKEY_verify_init(pkey_signer_ctx.get());
11✔
787
        if (ret <= 0) {
11✔
788
                return expected::unexpected(MakeError(
×
789
                        SetupError, "Failed to initialize the OpenSSL signer: " + GetOpenSSLErrorMessage()));
×
790
        }
791
        ret = EVP_PKEY_CTX_set_signature_md(pkey_signer_ctx.get(), EVP_sha256());
11✔
792
        if (ret <= 0) {
11✔
793
                return expected::unexpected(MakeError(
×
794
                        SetupError,
795
                        "Failed to set the OpenSSL signature to sha256: " + GetOpenSSLErrorMessage()));
×
796
        }
797

798
        // verify signature
799
        ret = EVP_PKEY_verify(
11✔
800
                pkey_signer_ctx.get(), signature.data(), signature.size(), shasum.data(), shasum.size());
801
        if (OpenSSLSignatureVerificationError(ret)) {
11✔
802
                log::Debug(
2✔
803
                        "Failed to verify the signature with the supported OpenSSL binary formats. Falling back to the custom Mender encoded binary format for ECDSA signatures: "
804
                        + GetOpenSSLErrorMessage());
4✔
805
                return VerifyECDSASignData(public_key_path, shasum, signature);
2✔
806
        }
807
        if (ret == OPENSSL_SUCCESS) {
9✔
808
                return true;
809
        }
810
        /* This is the case where ret == 0. The signature is simply wrong */
811
        return false;
812
}
813

814
expected::ExpectedBool VerifySign(
14✔
815
        const string &public_key_path, const mender::sha::SHA &shasum, const string &signature) {
816
        // signature: decode base64
817
        auto exp_decoded_signature = DecodeBase64(signature);
28✔
818
        if (!exp_decoded_signature) {
14✔
819
                return expected::unexpected(exp_decoded_signature.error());
×
820
        }
821
        auto decoded_signature = exp_decoded_signature.value();
14✔
822

823
        return VerifySignData(public_key_path, shasum, decoded_signature);
14✔
824
}
825

826
error::Error PrivateKey::SaveToPEM(const string &private_key_path) {
6✔
827
        auto bio_key = unique_ptr<BIO, void (*)(BIO *)>(
828
                BIO_new_file(private_key_path.c_str(), "w"), bio_free_func);
12✔
829
        if (bio_key == nullptr) {
6✔
830
                return MakeError(
831
                        SetupError,
832
                        "Failed to open the private key file (" + private_key_path
2✔
833
                                + "): " + GetOpenSSLErrorMessage());
4✔
834
        }
835

836
        // PEM_write_bio_PrivateKey_traditional will use the key-specific PKCS1
837
        // format if one is available for that key type, otherwise it will encode
838
        // to a PKCS8 key.
839
        auto ret = PEM_write_bio_PrivateKey_traditional(
5✔
840
                bio_key.get(), key.get(), nullptr, nullptr, 0, nullptr, nullptr);
841
        if (ret != OPENSSL_SUCCESS) {
5✔
842
                return MakeError(
843
                        SetupError,
844
                        "Failed to save the private key to file (" + private_key_path
×
845
                                + "): " + GetOpenSSLErrorMessage());
×
846
        }
847

848
        return error::NoError;
5✔
849
}
850

851
} // namespace crypto
852
} // namespace common
853
} // namespace mender
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc