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

mendersoftware / mender / 1569533599

22 Nov 2024 02:02PM UTC coverage: 79.882% (+0.02%) from 79.861%
1569533599

push

gitlab-ci

web-flow
Merge pull request #1697 from mendersoftware/cherry-4.0.x-master-priv_key_perms

[Cherry 4.0.x]: Fix: Private key file permissions

35 of 40 new or added lines in 4 files covered. (87.5%)

2 existing lines in 1 file now uncovered.

7179 of 8987 relevant lines covered (79.88%)

12177.76 hits per line

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

63.55
/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 <cerrno>
20
#include <cstdint>
21
#include <string>
22
#include <vector>
23
#include <memory>
24

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

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

41
#include <common/io.hpp>
42
#include <common/error.hpp>
43
#include <common/expected.hpp>
44
#include <common/common.hpp>
45
#include <common/path.hpp>
46

47
#include <artifact/sha/sha.hpp>
48

49

50
namespace mender {
51
namespace common {
52
namespace crypto {
53

54
const size_t MENDER_DIGEST_SHA256_LENGTH = 32;
55

56
const size_t OPENSSL_SUCCESS = 1;
57

58
using namespace std;
59

60
namespace error = mender::common::error;
61
namespace io = mender::common::io;
62
namespace path = mender::common::path;
63

64
using EnginePtr = unique_ptr<ENGINE, void (*)(ENGINE *)>;
65
#ifndef MENDER_CRYPTO_OPENSSL_LEGACY
66
using ProviderPtr = unique_ptr<OSSL_PROVIDER, int (*)(OSSL_PROVIDER *)>;
67
#endif // MENDER_CRYPTO_OPENSSL_LEGACY
68

69
class OpenSSLResourceHandle {
×
70
public:
71
        EnginePtr engine;
72
};
73

74
auto resource_handle_free_func = [](OpenSSLResourceHandle *h) {
×
75
        if (h) {
×
76
                delete h;
×
77
        }
78
};
×
79

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

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

116
        if (u == nullptr) {
3✔
117
                return 0;
118
        }
119

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

124
        return static_cast<int>(strnlen(pass, size));
3✔
125
};
126

127

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

136
        std::string errorDescription {};
137
        while (sslErrorCode != 0) {
56✔
138
                if (!errorDescription.empty()) {
39✔
139
                        errorDescription += '\n';
140
                }
141
                errorDescription += ERR_error_string(sslErrorCode, nullptr);
39✔
142
                sslErrorCode = ERR_get_error();
39✔
143
        }
144
        if (sysErrorCode != 0) {
17✔
145
                if (!errorDescription.empty()) {
15✔
146
                        errorDescription += '\n';
147
                }
148
                errorDescription += "System error, code=" + std::to_string(sysErrorCode);
30✔
149
                errorDescription += ", ";
15✔
150
                errorDescription += strerror(sysErrorCode);
15✔
151
        }
152
        return errorDescription;
17✔
153
}
154

155
ExpectedPrivateKey LoadFromHSMEngine(const Args &args) {
×
156
        log::Trace("Loading the private key from HSM");
×
157

158
        ENGINE_load_builtin_engines();
×
159
        auto engine = EnginePtr(ENGINE_by_id(args.ssl_engine.c_str()), engine_free_func);
×
160

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

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

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

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

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

210
        char *passphrase = const_cast<char *>(args.private_key_passphrase.c_str());
211

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

222
        return PrivateKey(std::move(private_key));
27✔
223
}
224
#endif // MENDER_CRYPTO_OPENSSL_LEGACY
225

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

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

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

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

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

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

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

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

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

301
        log::Trace("Loading private key");
74✔
302
        if (args.ssl_engine != "") {
37✔
303
                return LoadFromHSMEngine(args);
×
304
        }
305
        // Try to make sure the key has the right permissions and warn if there was
306
        // a change (MEN-7752), but try to load it in any case (it may not even be a
307
        // file on the file system).
308
        if (common::StartsWith<string>(args.private_key_path, "/")) {
37✔
309
                auto err = path::Permissions(
310
                        args.private_key_path,
311
                        {path::Perms::Owner_read, path::Perms::Owner_write},
312
                        path::WarnMode::WarnOnChange);
16✔
313
                if (err != error::NoError) {
8✔
314
                        log::Warning(
3✔
315
                                "Failed to fix permissions of the private key file '" + args.private_key_path
6✔
316
                                + "': " + err.String());
12✔
317
                }
318
        }
319
        return LoadFrom(args);
37✔
320
}
321

322
ExpectedPrivateKey PrivateKey::Generate(const unsigned int bits, const unsigned int exponent) {
8✔
323
#ifdef MENDER_CRYPTO_OPENSSL_LEGACY
324
        auto pkey_gen_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
325
                EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr), pkey_ctx_free_func);
16✔
326

327
        int ret = EVP_PKEY_keygen_init(pkey_gen_ctx.get());
8✔
328
        if (ret != OPENSSL_SUCCESS) {
8✔
329
                return expected::unexpected(MakeError(
×
330
                        SetupError,
331
                        "Failed to generate a private key. Initialization failed: "
332
                                + GetOpenSSLErrorMessage()));
×
333
        }
334

335
        ret = EVP_PKEY_CTX_set_rsa_keygen_bits(pkey_gen_ctx.get(), bits);
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

343
        auto exponent_bn = unique_ptr<BIGNUM, void (*)(BIGNUM *)>(BN_new(), bn_free);
16✔
344
        ret = BN_set_word(exponent_bn.get(), exponent);
8✔
345
        if (ret != OPENSSL_SUCCESS) {
8✔
346
                return expected::unexpected(MakeError(
×
347
                        SetupError,
348
                        "Failed to generate a private key. Parameters setting failed: "
349
                                + GetOpenSSLErrorMessage()));
×
350
        }
351

352
        ret = EVP_PKEY_CTX_set_rsa_keygen_pubexp(pkey_gen_ctx.get(), exponent_bn.get());
8✔
353
        if (ret != OPENSSL_SUCCESS) {
8✔
354
                return expected::unexpected(MakeError(
×
355
                        SetupError,
356
                        "Failed to generate a private key. Parameters setting failed: "
357
                                + GetOpenSSLErrorMessage()));
×
358
        }
359
        exponent_bn.release();
360

361
        EVP_PKEY *pkey = nullptr;
8✔
362
        ret = EVP_PKEY_keygen(pkey_gen_ctx.get(), &pkey);
8✔
363
        if (ret != OPENSSL_SUCCESS) {
8✔
364
                return expected::unexpected(MakeError(
×
365
                        SetupError,
366
                        "Failed to generate a private key. Generation failed: " + GetOpenSSLErrorMessage()));
×
367
        }
368
#else
369
        auto pkey_gen_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
370
                EVP_PKEY_CTX_new_from_name(nullptr, "RSA", nullptr), pkey_ctx_free_func);
371

372
        int ret = EVP_PKEY_keygen_init(pkey_gen_ctx.get());
373
        if (ret != OPENSSL_SUCCESS) {
374
                return expected::unexpected(MakeError(
375
                        SetupError,
376
                        "Failed to generate a private key. Initialization failed: "
377
                                + GetOpenSSLErrorMessage()));
378
        }
379

380
        OSSL_PARAM params[3];
381
        auto bits_buffer = bits;
382
        auto exponent_buffer = exponent;
383
        params[0] = OSSL_PARAM_construct_uint("bits", &bits_buffer);
384
        params[1] = OSSL_PARAM_construct_uint("e", &exponent_buffer);
385
        params[2] = OSSL_PARAM_construct_end();
386

387
        ret = EVP_PKEY_CTX_set_params(pkey_gen_ctx.get(), params);
388
        if (ret != OPENSSL_SUCCESS) {
389
                return expected::unexpected(MakeError(
390
                        SetupError,
391
                        "Failed to generate a private key. Parameters setting failed: "
392
                                + GetOpenSSLErrorMessage()));
393
        }
394

395
        EVP_PKEY *pkey = nullptr;
396
        ret = EVP_PKEY_generate(pkey_gen_ctx.get(), &pkey);
397
        if (ret != OPENSSL_SUCCESS) {
398
                return expected::unexpected(MakeError(
399
                        SetupError,
400
                        "Failed to generate a private key. Generation failed: " + GetOpenSSLErrorMessage()));
401
        }
402
#endif
403

404
        auto private_key = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(pkey, pkey_free_func);
8✔
405
        return PrivateKey(std::move(private_key));
8✔
406
}
407

408
expected::ExpectedString EncodeBase64(vector<uint8_t> to_encode) {
14✔
409
        // Predict the len of the decoded for later verification. From man page:
410
        // For every 3 bytes of input provided 4 bytes of output
411
        // data will be produced. If n is not divisible by 3 (...)
412
        // the output is padded such that it is always divisible by 4.
413
        const size_t predicted_len {4 * ((to_encode.size() + 2) / 3)};
14✔
414

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

419
        const int64_t output_len {
420
                EVP_EncodeBlock(buffer.data(), to_encode.data(), static_cast<int>(to_encode.size()))};
14✔
421
        assert(output_len >= 0);
422

423
        if (predicted_len != static_cast<uint64_t>(output_len)) {
14✔
424
                return expected::unexpected(
×
425
                        MakeError(Base64Error, "The predicted and the actual length differ"));
×
426
        }
427

428
        return string(buffer.begin(), buffer.end() - 1); // Remove the last zero byte
28✔
429
}
430

431
expected::ExpectedBytes DecodeBase64(string to_decode) {
15✔
432
        // Predict the len of the decoded for later verification. From man page:
433
        // For every 4 input bytes exactly 3 output bytes will be
434
        // produced. The output will be padded with 0 bits if necessary
435
        // to ensure that the output is always 3 bytes.
436
        const size_t predicted_len {3 * ((to_decode.size() + 3) / 4)};
15✔
437

438
        auto buffer {vector<unsigned char>(predicted_len)};
15✔
439

440
        const int64_t output_len {EVP_DecodeBlock(
15✔
441
                buffer.data(),
442
                common::ByteVectorFromString(to_decode).data(),
15✔
443
                static_cast<int>(to_decode.size()))};
15✔
444
        assert(output_len >= 0);
445

446
        if (predicted_len != static_cast<uint64_t>(output_len)) {
15✔
447
                return expected::unexpected(MakeError(
×
448
                        Base64Error,
449
                        "The predicted (" + std::to_string(predicted_len) + ") and the actual ("
×
450
                                + std::to_string(output_len) + ") length differ"));
×
451
        }
452

453
        // Subtract padding bytes. Inspired by internal OpenSSL code from:
454
        // https://github.com/openssl/openssl/blob/ff88545e02ab48a52952350c52013cf765455dd3/crypto/ct/ct_b64.c#L46
455
        for (auto it = to_decode.crbegin(); *it == '='; it++) {
24✔
456
                buffer.pop_back();
457
        }
458

459
        return buffer;
15✔
460
}
461

462

463
expected::ExpectedString ExtractPublicKey(const Args &args) {
9✔
464
        auto exp_private_key = PrivateKey::Load(args);
9✔
465
        if (!exp_private_key) {
9✔
466
                return expected::unexpected(exp_private_key.error());
2✔
467
        }
468

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

471
        if (!bio_public_key.get()) {
8✔
472
                return expected::unexpected(MakeError(
×
473
                        SetupError,
474
                        "Failed to extract the public key from the private key " + args.private_key_path
×
475
                                + "):" + GetOpenSSLErrorMessage()));
×
476
        }
477

478
        int ret = PEM_write_bio_PUBKEY(bio_public_key.get(), exp_private_key.value().Get());
8✔
479
        if (ret != OPENSSL_SUCCESS) {
8✔
480
                return expected::unexpected(MakeError(
×
481
                        SetupError,
482
                        "Failed to extract the public key from the private key (" + args.private_key_path
×
483
                                + "): OpenSSL BIO write failed: " + GetOpenSSLErrorMessage()));
×
484
        }
485

486
        // NOTE: At this point we already have a public key available for extraction.
487
        // However, when using some providers in OpenSSL3 the external provider might
488
        // write the key in the old PKCS#1 format. The format is not deprecated, but
489
        // our older backends only understand the format if it is in the PKCS#8
490
        // (SubjectPublicKey) format:
491
        //
492
        // For us who don't speak OpenSSL:
493
        //
494
        // -- BEGIN RSA PUBLIC KEY -- <- PKCS#1 (old format)
495
        // -- BEGIN PUBLIC KEY -- <- PKCS#8 (new format - can hold different key types)
496

497

498
        auto evp_public_key = PkeyPtr(
499
                PEM_read_bio_PUBKEY(bio_public_key.get(), nullptr, nullptr, nullptr), pkey_free_func);
16✔
500

501
        if (evp_public_key == nullptr) {
8✔
502
                return expected::unexpected(MakeError(
×
503
                        SetupError,
504
                        "Failed to extract the public key from the private key " + args.private_key_path
×
505
                                + "):" + GetOpenSSLErrorMessage()));
×
506
        }
507

508
        auto bio_public_key_new =
509
                unique_ptr<BIO, void (*)(BIO *)>(BIO_new(BIO_s_mem()), bio_free_all_func);
16✔
510

511
        if (bio_public_key_new == nullptr) {
8✔
512
                return expected::unexpected(MakeError(
×
513
                        SetupError,
514
                        "Failed to extract the public key from the public key " + args.private_key_path
×
515
                                + "):" + GetOpenSSLErrorMessage()));
×
516
        }
517

518
        ret = PEM_write_bio_PUBKEY(bio_public_key_new.get(), evp_public_key.get());
8✔
519
        if (ret != OPENSSL_SUCCESS) {
8✔
520
                return expected::unexpected(MakeError(
×
521
                        SetupError,
522
                        "Failed to extract the public key from the private key: (" + args.private_key_path
×
523
                                + "): OpenSSL BIO write failed: " + GetOpenSSLErrorMessage()));
×
524
        }
525

526
        // Inconsistent API, this returns size_t, but the API below uses int. Should not matter for
527
        // key sizes though.
528
        int pending = static_cast<int>(BIO_ctrl_pending(bio_public_key_new.get()));
8✔
529
        if (pending <= 0) {
8✔
530
                return expected::unexpected(MakeError(
×
531
                        SetupError,
532
                        "Failed to extract the public key from bio ctrl: (" + args.private_key_path
×
533
                                + "): Zero byte key unexpected: " + GetOpenSSLErrorMessage()));
×
534
        }
535

536
        vector<uint8_t> key_vector(pending);
8✔
537

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

540
        if (read == 0) {
8✔
541
                MakeError(
×
542
                        SetupError,
543
                        "Failed to extract the public key from (" + args.private_key_path
×
544
                                + "): Zero bytes read from BIO: " + GetOpenSSLErrorMessage());
×
545
        }
546

547
        return string(key_vector.begin(), key_vector.end());
16✔
548
}
549

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

553
        auto md_ctx = unique_ptr<EVP_MD_CTX, void (*)(EVP_MD_CTX *)>(EVP_MD_CTX_new(), EVP_MD_CTX_free);
2✔
554
        if (md_ctx == nullptr) {
1✔
555
                return expected::unexpected(MakeError(
×
556
                        SetupError, "Failed to initialize the OpenSSL md_ctx: " + GetOpenSSLErrorMessage()));
×
557
        }
558

559
        int ret {EVP_DigestSignInit(md_ctx.get(), nullptr, nullptr, nullptr, pkey)};
1✔
560
        if (ret != OPENSSL_SUCCESS) {
1✔
561
                return expected::unexpected(MakeError(
×
562
                        SetupError, "Failed to initialize the OpenSSL signature: " + GetOpenSSLErrorMessage()));
×
563
        }
564

565
        /* Calculate the required size for the signature by passing a nullptr buffer */
566
        ret = EVP_DigestSign(md_ctx.get(), nullptr, &sig_len, raw_data.data(), raw_data.size());
1✔
567
        if (ret != OPENSSL_SUCCESS) {
1✔
568
                return expected::unexpected(MakeError(
×
569
                        SetupError,
570
                        "Failed to find the required size of the signature buffer: "
571
                                + GetOpenSSLErrorMessage()));
×
572
        }
573

574
        vector<uint8_t> sig(sig_len);
1✔
575
        ret = EVP_DigestSign(md_ctx.get(), sig.data(), &sig_len, raw_data.data(), raw_data.size());
1✔
576
        if (ret != OPENSSL_SUCCESS) {
1✔
577
                return expected::unexpected(
×
578
                        MakeError(SetupError, "Failed to sign the message: " + GetOpenSSLErrorMessage()));
×
579
        }
580

581
        // The signature may in some cases be shorter than the previously allocated
582
        // length (which is the max)
583
        sig.resize(sig_len);
1✔
584

585
        return sig;
1✔
586
}
587

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

592
        if (EVP_PKEY_sign_init(pkey_signer_ctx.get()) <= 0) {
9✔
593
                return expected::unexpected(MakeError(
×
594
                        SetupError, "Failed to initialize the OpenSSL signer: " + GetOpenSSLErrorMessage()));
×
595
        }
596
        if (EVP_PKEY_CTX_set_signature_md(pkey_signer_ctx.get(), EVP_sha256()) <= 0) {
9✔
597
                return expected::unexpected(MakeError(
×
598
                        SetupError,
599
                        "Failed to set the OpenSSL signature to sha256: " + GetOpenSSLErrorMessage()));
×
600
        }
601

602
        vector<uint8_t> signature {};
603

604
        // Set the needed signature buffer length
605
        size_t digestlength = MENDER_DIGEST_SHA256_LENGTH, siglength;
606
        if (EVP_PKEY_sign(pkey_signer_ctx.get(), nullptr, &siglength, digest.data(), digestlength)
9✔
607
                <= 0) {
608
                return expected::unexpected(MakeError(
×
609
                        SetupError, "Failed to get the signature buffer length: " + GetOpenSSLErrorMessage()));
×
610
        }
611
        signature.resize(siglength);
9✔
612

613
        if (EVP_PKEY_sign(
9✔
614
                        pkey_signer_ctx.get(), signature.data(), &siglength, digest.data(), digestlength)
615
                <= 0) {
616
                return expected::unexpected(
×
617
                        MakeError(SetupError, "Failed to sign the digest: " + GetOpenSSLErrorMessage()));
×
618
        }
619

620
        // The signature may in some cases be shorter than the previously allocated
621
        // length (which is the max)
622
        signature.resize(siglength);
9✔
623

624
        return signature;
9✔
625
}
626

627
expected::ExpectedBytes SignData(const Args &args, const vector<uint8_t> &raw_data) {
11✔
628
        auto exp_private_key = PrivateKey::Load(args);
11✔
629
        if (!exp_private_key) {
11✔
630
                return expected::unexpected(exp_private_key.error());
2✔
631
        }
632

633
        log::Info("Signing with: " + args.private_key_path);
10✔
634

635
        auto key_type = EVP_PKEY_base_id(exp_private_key.value().Get());
10✔
636

637
        // ED25519 signatures need to be handled independently, because of how the
638
        // signature scheme is designed.
639
        if (key_type == EVP_PKEY_ED25519) {
10✔
640
                return SignED25519(exp_private_key.value().Get(), raw_data);
1✔
641
        }
642

643
        auto exp_shasum = mender::sha::Shasum(raw_data);
9✔
644
        if (!exp_shasum) {
9✔
645
                return expected::unexpected(exp_shasum.error());
×
646
        }
647
        auto digest = exp_shasum.value(); /* The shasummed data = digest in crypto world */
9✔
648
        log::Debug("Shasum is: " + digest.String());
18✔
649

650
        return SignGeneric(exp_private_key.value(), digest);
18✔
651
}
652

653
expected::ExpectedString Sign(const Args &args, const vector<uint8_t> &raw_data) {
11✔
654
        auto exp_signed_data = SignData(args, raw_data);
11✔
655
        if (!exp_signed_data) {
11✔
656
                return expected::unexpected(exp_signed_data.error());
2✔
657
        }
658
        vector<uint8_t> signature = exp_signed_data.value();
10✔
659

660
        return EncodeBase64(signature);
20✔
661
}
662

663
const size_t mender_decode_buf_size = 256;
664
const size_t ecdsa256keySize = 32;
665

666
// Try and decode the keys from pure binary, assuming that the points on the
667
// curve (r,s), have been concatenated together (r || s), and simply dumped to
668
// binary. Which is what we did in the `mender-artifact` tool.
669
// (See MEN-1740) for some insight into previous issues, and the chosen fix.
670
static expected::ExpectedBytes TryASN1EncodeMenderCustomBinaryECFormat(
2✔
671
        const vector<uint8_t> &signature,
672
        const mender::sha::SHA &shasum,
673
        std::function<BIGNUM *(const unsigned char *signature, int length, BIGNUM *_unused)>
674
                BinaryDecoderFn) {
675
        // Verify that the marshalled keys match our expectation
676
        const size_t assumed_signature_size {2 * ecdsa256keySize};
677
        if (signature.size() > assumed_signature_size) {
2✔
678
                return expected::unexpected(MakeError(
×
679
                        SetupError,
680
                        "Unexpected size of the signature for ECDSA. Expected 2*" + to_string(ecdsa256keySize)
×
681
                                + " bytes. Got: " + to_string(signature.size())));
×
682
        }
683
        auto ecSig = unique_ptr<ECDSA_SIG, void (*)(ECDSA_SIG *)>(ECDSA_SIG_new(), ECDSA_SIG_free);
4✔
684
        if (ecSig == nullptr) {
2✔
685
                return expected::unexpected(MakeError(
×
686
                        SetupError,
687
                        "Failed to allocate the structure for the ECDSA signature: "
688
                                + GetOpenSSLErrorMessage()));
×
689
        }
690

691
        auto r = unique_ptr<BIGNUM, void (*)(BIGNUM *)>(
692
                BinaryDecoderFn(signature.data(), ecdsa256keySize, nullptr /* allocate new memory for r */),
693
                bn_free);
4✔
694
        if (r == nullptr) {
2✔
695
                return expected::unexpected(MakeError(
×
696
                        SetupError,
697
                        "Failed to extract the r(andom) part from the ECDSA signature in the binary representation: "
698
                                + GetOpenSSLErrorMessage()));
×
699
        }
700
        auto s = unique_ptr<BIGNUM, void (*)(BIGNUM *)>(
701
                BinaryDecoderFn(
702
                        signature.data() + ecdsa256keySize,
703
                        ecdsa256keySize,
704
                        nullptr /* allocate new memory for s */),
705
                bn_free);
4✔
706
        if (s == nullptr) {
2✔
707
                return expected::unexpected(MakeError(
×
708
                        SetupError,
709
                        "Failed to extract the s(ignature) part from the ECDSA signature in the binary representation: "
710
                                + GetOpenSSLErrorMessage()));
×
711
        }
712

713
        // Set the r&s values in the SIG struct
714
        // r & s now owned by ecSig
715
        int ret {ECDSA_SIG_set0(ecSig.get(), r.get(), s.get())};
2✔
716
        if (ret != OPENSSL_SUCCESS) {
2✔
717
                return expected::unexpected(MakeError(
×
718
                        SetupError,
719
                        "Failed to set the signature parts in the ECDSA structure: "
720
                                + GetOpenSSLErrorMessage()));
×
721
        }
722
        r.release();
723
        s.release();
724

725
        /* Allocate some array guaranteed to hold the DER-encoded structure */
726
        vector<uint8_t> der_encoded_byte_array(mender_decode_buf_size);
2✔
727
        unsigned char *arr_p = &der_encoded_byte_array[0];
2✔
728
        int len = i2d_ECDSA_SIG(ecSig.get(), &arr_p);
2✔
729
        if (len < 0) {
2✔
730
                return expected::unexpected(MakeError(
×
731
                        SetupError,
732
                        "Failed to set the signature parts in the ECDSA structure: "
733
                                + GetOpenSSLErrorMessage()));
×
734
        }
735
        /* Resize to the actual size of the DER-encoded signature */
736
        der_encoded_byte_array.resize(len);
2✔
737

738
        return der_encoded_byte_array;
2✔
739
}
740

741

742
expected::ExpectedBool VerifySignData(
743
        const string &public_key_path,
744
        const mender::sha::SHA &shasum,
745
        const vector<uint8_t> &signature);
746

747
static expected::ExpectedBool VerifyECDSASignData(
2✔
748
        const string &public_key_path,
749
        const mender::sha::SHA &shasum,
750
        const vector<uint8_t> &signature) {
751
        expected::ExpectedBytes exp_der_encoded_signature =
752
                TryASN1EncodeMenderCustomBinaryECFormat(signature, shasum, BN_bin2bn)
4✔
753
                        .or_else([&signature, &shasum](error::Error big_endian_error) {
×
754
                                log::Debug(
×
755
                                        "Failed to decode the signature binary blob from our custom binary format assuming the big-endian encoding, error: "
756
                                        + big_endian_error.String()
×
757
                                        + " falling back and trying anew assuming it is little-endian encoded: ");
×
758
                                return TryASN1EncodeMenderCustomBinaryECFormat(signature, shasum, BN_lebin2bn);
×
759
                        });
2✔
760
        if (!exp_der_encoded_signature) {
2✔
761
                return expected::unexpected(
×
762
                        MakeError(VerificationError, exp_der_encoded_signature.error().message));
×
763
        }
764

765
        vector<uint8_t> der_encoded_signature = exp_der_encoded_signature.value();
2✔
766

767
        return VerifySignData(public_key_path, shasum, der_encoded_signature);
2✔
768
}
769

770
static bool OpenSSLSignatureVerificationError(int a) {
771
        /*
772
         * The signature check errored. This is different from the signature being
773
         * wrong. We simply were not able to perform the check in this instance.
774
         * Therefore, we fall back to trying the custom marshalled binary ECDSA
775
         * signature, which we have been using in Mender.
776
         */
777
        return a < 0;
778
}
779

780
expected::ExpectedBool VerifySignData(
16✔
781
        const string &public_key_path,
782
        const mender::sha::SHA &shasum,
783
        const vector<uint8_t> &signature) {
784
        auto bio_key =
785
                unique_ptr<BIO, void (*)(BIO *)>(BIO_new_file(public_key_path.c_str(), "r"), bio_free_func);
32✔
786
        if (bio_key == nullptr) {
16✔
787
                return expected::unexpected(MakeError(
3✔
788
                        SetupError,
789
                        "Failed to open the public key file from (" + public_key_path
6✔
790
                                + "):" + GetOpenSSLErrorMessage()));
15✔
791
        }
792

793
        auto pkey = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
794
                PEM_read_bio_PUBKEY(bio_key.get(), nullptr, nullptr, nullptr), pkey_free_func);
26✔
795
        if (pkey == nullptr) {
13✔
796
                return expected::unexpected(MakeError(
2✔
797
                        SetupError,
798
                        "Failed to load the public key from(" + public_key_path
4✔
799
                                + "): " + GetOpenSSLErrorMessage()));
10✔
800
        }
801

802
        auto pkey_signer_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
803
                EVP_PKEY_CTX_new(pkey.get(), nullptr), pkey_ctx_free_func);
22✔
804

805
        auto ret = EVP_PKEY_verify_init(pkey_signer_ctx.get());
11✔
806
        if (ret <= 0) {
11✔
807
                return expected::unexpected(MakeError(
×
808
                        SetupError, "Failed to initialize the OpenSSL signer: " + GetOpenSSLErrorMessage()));
×
809
        }
810
        ret = EVP_PKEY_CTX_set_signature_md(pkey_signer_ctx.get(), EVP_sha256());
11✔
811
        if (ret <= 0) {
11✔
812
                return expected::unexpected(MakeError(
×
813
                        SetupError,
814
                        "Failed to set the OpenSSL signature to sha256: " + GetOpenSSLErrorMessage()));
×
815
        }
816

817
        // verify signature
818
        ret = EVP_PKEY_verify(
11✔
819
                pkey_signer_ctx.get(), signature.data(), signature.size(), shasum.data(), shasum.size());
820
        if (OpenSSLSignatureVerificationError(ret)) {
11✔
821
                log::Debug(
2✔
822
                        "Failed to verify the signature with the supported OpenSSL binary formats. Falling back to the custom Mender encoded binary format for ECDSA signatures: "
823
                        + GetOpenSSLErrorMessage());
4✔
824
                return VerifyECDSASignData(public_key_path, shasum, signature);
2✔
825
        }
826
        if (ret == OPENSSL_SUCCESS) {
9✔
827
                return true;
828
        }
829
        /* This is the case where ret == 0. The signature is simply wrong */
830
        return false;
831
}
832

833
expected::ExpectedBool VerifySign(
14✔
834
        const string &public_key_path, const mender::sha::SHA &shasum, const string &signature) {
835
        // signature: decode base64
836
        auto exp_decoded_signature = DecodeBase64(signature);
28✔
837
        if (!exp_decoded_signature) {
14✔
838
                return expected::unexpected(exp_decoded_signature.error());
×
839
        }
840
        auto decoded_signature = exp_decoded_signature.value();
14✔
841

842
        return VerifySignData(public_key_path, shasum, decoded_signature);
14✔
843
}
844

845
error::Error PrivateKey::SaveToPEM(const string &private_key_path) {
7✔
846
        if (path::FileExists(private_key_path)) {
7✔
847
                auto err = path::FileDelete(private_key_path);
3✔
848
                if (err != error::NoError) {
3✔
NEW
849
                        log::Debug(
×
NEW
850
                                "Failed to delete the private key fie '" + private_key_path + "': " + err.String());
×
851
                }
852
        }
853
        auto ex_fd =
854
                path::FileCreate(private_key_path, {path::Perms::Owner_read, path::Perms::Owner_write});
14✔
855
        if (!ex_fd) {
7✔
856
                auto &err = ex_fd.error();
857
                return err.FollowedBy(MakeError(
1✔
858
                        SetupError, "Failed to create the private key file '" + private_key_path + "'"));
2✔
859
        }
860
        auto bio_key =
861
                unique_ptr<BIO, void (*)(BIO *)>(BIO_new_fd(ex_fd.value(), BIO_CLOSE), bio_free_func);
12✔
862
        if (bio_key == nullptr) {
6✔
863
                return MakeError(
864
                        SetupError,
UNCOV
865
                        "Failed to open the private key file (" + private_key_path
×
UNCOV
866
                                + "): " + GetOpenSSLErrorMessage());
×
867
        }
868

869
        // PEM_write_bio_PrivateKey_traditional will use the key-specific PKCS1
870
        // format if one is available for that key type, otherwise it will encode
871
        // to a PKCS8 key.
872
        auto ret = PEM_write_bio_PrivateKey_traditional(
6✔
873
                bio_key.get(), key.get(), nullptr, nullptr, 0, nullptr, nullptr);
874
        if (ret != OPENSSL_SUCCESS) {
6✔
875
                return MakeError(
876
                        SetupError,
877
                        "Failed to save the private key to file (" + private_key_path
×
878
                                + "): " + GetOpenSSLErrorMessage());
×
879
        }
880

881
        return error::NoError;
6✔
882
}
883

884
} // namespace crypto
885
} // namespace common
886
} // 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

© 2025 Coveralls, Inc