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

mendersoftware / mender / 1478591854

02 Oct 2024 11:28AM UTC coverage: 76.31%. First build
1478591854

push

gitlab-ci

danielskinstad
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/1.1.1/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: Title

Signed-off-by: Daniel Skinstad Drabitzius <daniel.drabitzius@northern.tech>

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

7367 of 9654 relevant lines covered (76.31%)

11319.55 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) {
59✔
135
                if (!errorDescription.empty()) {
41✔
136
                        errorDescription += '\n';
137
                }
138
                errorDescription += ERR_error_string(sslErrorCode, nullptr);
41✔
139
                sslErrorCode = ERR_get_error();
41✔
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/1.1.1/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✔
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