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

mendersoftware / mender / 1480778148

03 Oct 2024 06:26PM UTC coverage: 76.372% (+0.06%) from 76.31%
1480778148

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/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>

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

97 existing lines in 3 files now uncovered.

7350 of 9624 relevant lines covered (76.37%)

11242.72 hits per line

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

63.49
/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

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

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

77
auto pkey_ctx_free_func = [](EVP_PKEY_CTX *ctx) {
26✔
78
        if (ctx) {
26✔
79
                EVP_PKEY_CTX_free(ctx);
26✔
80
        }
81
};
26✔
82
auto pkey_free_func = [](EVP_PKEY *key) {
53✔
83
        if (key) {
53✔
84
                EVP_PKEY_free(key);
53✔
85
        }
86
};
53✔
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✔
UNCOV
97
auto bn_free = [](BIGNUM *bn) {
×
98
        if (bn) {
×
99
                BN_free(bn);
×
100
        }
UNCOV
101
};
×
102
auto engine_free_func = [](ENGINE *e) {
×
103
        if (e) {
×
104
                ENGINE_free(e);
×
105
        }
UNCOV
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

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

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

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

UNCOV
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): "
UNCOV
171
                                + GetOpenSSLErrorMessage()));
×
172
        }
UNCOV
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 */),
UNCOV
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: "
UNCOV
186
                                + GetOpenSSLErrorMessage()));
×
187
        }
UNCOV
188
        log::Debug("Successfully loaded the private key from the HSM Engine: " + args.ssl_engine);
×
189

190
        auto handle = unique_ptr<OpenSSLResourceHandle, void (*)(OpenSSLResourceHandle *)>(
UNCOV
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✔
UNCOV
300
                return LoadFromHSMEngine(args);
×
301
        }
302
        return LoadFrom(args);
37✔
303
}
304

305
ExpectedPrivateKey PrivateKey::Generate() {
7✔
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_ED25519, nullptr), pkey_ctx_free_func);
14✔
309
#else
310
        auto pkey_gen_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
311
                EVP_PKEY_CTX_new_from_name(nullptr, "ED25519", nullptr), pkey_ctx_free_func);
312
#endif // MENDER_CRYPTO_OPENSSL_LEGACY
313

314
        int ret = EVP_PKEY_keygen_init(pkey_gen_ctx.get());
7✔
315
        if (ret != OPENSSL_SUCCESS) {
7✔
UNCOV
316
                return expected::unexpected(MakeError(
×
317
                        SetupError,
318
                        "Failed to generate a private key. Initialization failed: "
UNCOV
319
                                + GetOpenSSLErrorMessage()));
×
320
        }
321
        EVP_PKEY *pkey = nullptr;
7✔
322
#ifdef MENDER_CRYPTO_OPENSSL_LEGACY
323
        ret = EVP_PKEY_keygen(pkey_gen_ctx.get(), &pkey);
7✔
324
#else
325
        ret = EVP_PKEY_generate(pkey_gen_ctx.get(), &pkey);
326
#endif // MENDER_CRYPTO_OPENSSL_LEGACY
327
        if (ret != OPENSSL_SUCCESS) {
7✔
328
                return expected::unexpected(MakeError(
×
329
                        SetupError,
UNCOV
330
                        "Failed to generate a private key. Generation failed: " + GetOpenSSLErrorMessage()));
×
331
        }
332

333
        auto private_key = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(pkey, pkey_free_func);
7✔
334
        return PrivateKey(std::move(private_key));
7✔
335
}
336

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

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

348
        const int64_t output_len {
349
                EVP_EncodeBlock(buffer.data(), to_encode.data(), static_cast<int>(to_encode.size()))};
14✔
350
        assert(output_len >= 0);
351

352
        if (predicted_len != static_cast<uint64_t>(output_len)) {
14✔
UNCOV
353
                return expected::unexpected(
×
UNCOV
354
                        MakeError(Base64Error, "The predicted and the actual length differ"));
×
355
        }
356

357
        return string(buffer.begin(), buffer.end() - 1); // Remove the last zero byte
28✔
358
}
359

360
expected::ExpectedBytes DecodeBase64(string to_decode) {
15✔
361
        // Predict the len of the decoded for later verification. From man page:
362
        // For every 4 input bytes exactly 3 output bytes will be
363
        // produced. The output will be padded with 0 bits if necessary
364
        // to ensure that the output is always 3 bytes.
365
        const uint64_t predicted_len {3 * ((to_decode.size() + 3) / 4)};
15✔
366

367
        auto buffer {vector<unsigned char>(predicted_len)};
15✔
368

369
        const int64_t output_len {EVP_DecodeBlock(
15✔
370
                buffer.data(),
371
                common::ByteVectorFromString(to_decode).data(),
15✔
372
                static_cast<int>(to_decode.size()))};
15✔
373
        assert(output_len >= 0);
374

375
        if (predicted_len != static_cast<uint64_t>(output_len)) {
15✔
UNCOV
376
                return expected::unexpected(MakeError(
×
377
                        Base64Error,
UNCOV
378
                        "The predicted (" + std::to_string(predicted_len) + ") and the actual ("
×
UNCOV
379
                                + std::to_string(output_len) + ") length differ"));
×
380
        }
381

382
        // Subtract padding bytes. Inspired by internal OpenSSL code from:
383
        // https://github.com/openssl/openssl/blob/ff88545e02ab48a52952350c52013cf765455dd3/crypto/ct/ct_b64.c#L46
384
        for (auto it = to_decode.crbegin(); *it == '='; it++) {
24✔
385
                buffer.pop_back();
386
        }
387

388
        return buffer;
15✔
389
}
390

391

392
expected::ExpectedString ExtractPublicKey(const Args &args) {
9✔
393
        auto exp_private_key = PrivateKey::Load(args);
9✔
394
        if (!exp_private_key) {
9✔
395
                return expected::unexpected(exp_private_key.error());
2✔
396
        }
397

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

400
        if (!bio_public_key.get()) {
8✔
UNCOV
401
                return expected::unexpected(MakeError(
×
402
                        SetupError,
UNCOV
403
                        "Failed to extract the public key from the private key " + args.private_key_path
×
UNCOV
404
                                + "):" + GetOpenSSLErrorMessage()));
×
405
        }
406

407
        int ret = PEM_write_bio_PUBKEY(bio_public_key.get(), exp_private_key.value().Get());
8✔
408
        if (ret != OPENSSL_SUCCESS) {
8✔
UNCOV
409
                return expected::unexpected(MakeError(
×
410
                        SetupError,
UNCOV
411
                        "Failed to extract the public key from the private key (" + args.private_key_path
×
UNCOV
412
                                + "): OpenSSL BIO write failed: " + GetOpenSSLErrorMessage()));
×
413
        }
414

415
        // NOTE: At this point we already have a public key available for extraction.
416
        // However, when using some providers in OpenSSL3 the external provider might
417
        // write the key in the old PKCS#1 format. The format is not deprecated, but
418
        // our older backends only understand the format if it is in the PKCS#8
419
        // (SubjectPublicKey) format:
420
        //
421
        // For us who don't speak OpenSSL:
422
        //
423
        // -- BEGIN RSA PUBLIC KEY -- <- PKCS#1 (old format)
424
        // -- BEGIN PUBLIC KEY -- <- PKCS#8 (new format - can hold different key types)
425

426

427
        auto evp_public_key = PkeyPtr(
428
                PEM_read_bio_PUBKEY(bio_public_key.get(), nullptr, nullptr, nullptr), pkey_free_func);
16✔
429

430
        if (evp_public_key == nullptr) {
8✔
UNCOV
431
                return expected::unexpected(MakeError(
×
432
                        SetupError,
UNCOV
433
                        "Failed to extract the public key from the private key " + args.private_key_path
×
UNCOV
434
                                + "):" + GetOpenSSLErrorMessage()));
×
435
        }
436

437
        auto bio_public_key_new =
438
                unique_ptr<BIO, void (*)(BIO *)>(BIO_new(BIO_s_mem()), bio_free_all_func);
16✔
439

440
        if (bio_public_key_new == nullptr) {
8✔
441
                return expected::unexpected(MakeError(
×
442
                        SetupError,
UNCOV
443
                        "Failed to extract the public key from the public key " + args.private_key_path
×
UNCOV
444
                                + "):" + GetOpenSSLErrorMessage()));
×
445
        }
446

447
        ret = PEM_write_bio_PUBKEY(bio_public_key_new.get(), evp_public_key.get());
8✔
448
        if (ret != OPENSSL_SUCCESS) {
8✔
UNCOV
449
                return expected::unexpected(MakeError(
×
450
                        SetupError,
UNCOV
451
                        "Failed to extract the public key from the private key: (" + args.private_key_path
×
UNCOV
452
                                + "): OpenSSL BIO write failed: " + GetOpenSSLErrorMessage()));
×
453
        }
454

455
        int pending = BIO_ctrl_pending(bio_public_key_new.get());
8✔
456
        if (pending <= 0) {
8✔
UNCOV
457
                return expected::unexpected(MakeError(
×
458
                        SetupError,
UNCOV
459
                        "Failed to extract the public key from bio ctrl: (" + args.private_key_path
×
UNCOV
460
                                + "): Zero byte key unexpected: " + GetOpenSSLErrorMessage()));
×
461
        }
462

463
        vector<uint8_t> key_vector(pending);
8✔
464

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

467
        if (read == 0) {
8✔
UNCOV
468
                MakeError(
×
469
                        SetupError,
UNCOV
470
                        "Failed to extract the public key from (" + args.private_key_path
×
471
                                + "): Zero bytes read from BIO: " + GetOpenSSLErrorMessage());
×
472
        }
473

474
        return string(key_vector.begin(), key_vector.end());
16✔
475
}
476

477
static expected::ExpectedBytes SignED25519(EVP_PKEY *pkey, const vector<uint8_t> &raw_data) {
2✔
478
        size_t sig_len;
479

480
        auto md_ctx = unique_ptr<EVP_MD_CTX, void (*)(EVP_MD_CTX *)>(EVP_MD_CTX_new(), EVP_MD_CTX_free);
4✔
481
        if (md_ctx == nullptr) {
2✔
UNCOV
482
                return expected::unexpected(MakeError(
×
UNCOV
483
                        SetupError, "Failed to initialize the OpenSSL md_ctx: " + GetOpenSSLErrorMessage()));
×
484
        }
485

486
        int ret {EVP_DigestSignInit(md_ctx.get(), nullptr, nullptr, nullptr, pkey)};
2✔
487
        if (ret != OPENSSL_SUCCESS) {
2✔
UNCOV
488
                return expected::unexpected(MakeError(
×
UNCOV
489
                        SetupError, "Failed to initialize the OpenSSL signature: " + GetOpenSSLErrorMessage()));
×
490
        }
491

492
        /* Calculate the required size for the signature by passing a nullptr buffer */
493
        ret = EVP_DigestSign(md_ctx.get(), nullptr, &sig_len, raw_data.data(), raw_data.size());
2✔
494
        if (ret != OPENSSL_SUCCESS) {
2✔
495
                return expected::unexpected(MakeError(
×
496
                        SetupError,
497
                        "Failed to find the required size of the signature buffer: "
UNCOV
498
                                + GetOpenSSLErrorMessage()));
×
499
        }
500

501
        vector<uint8_t> sig(sig_len);
2✔
502
        ret = EVP_DigestSign(md_ctx.get(), sig.data(), &sig_len, raw_data.data(), raw_data.size());
2✔
503
        if (ret != OPENSSL_SUCCESS) {
2✔
UNCOV
504
                return expected::unexpected(
×
505
                        MakeError(SetupError, "Failed to sign the message: " + GetOpenSSLErrorMessage()));
×
506
        }
507

508
        // The signature may in some cases be shorter than the previously allocated
509
        // length (which is the max)
510
        sig.resize(sig_len);
2✔
511

512
        return sig;
2✔
513
}
514

515
expected::ExpectedBytes SignGeneric(PrivateKey &private_key, const vector<uint8_t> &digest) {
8✔
516
        auto pkey_signer_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
517
                EVP_PKEY_CTX_new(private_key.Get(), nullptr), pkey_ctx_free_func);
16✔
518

519
        if (EVP_PKEY_sign_init(pkey_signer_ctx.get()) <= 0) {
8✔
UNCOV
520
                return expected::unexpected(MakeError(
×
521
                        SetupError, "Failed to initialize the OpenSSL signer: " + GetOpenSSLErrorMessage()));
×
522
        }
523
        if (EVP_PKEY_CTX_set_signature_md(pkey_signer_ctx.get(), EVP_sha256()) <= 0) {
8✔
UNCOV
524
                return expected::unexpected(MakeError(
×
525
                        SetupError,
UNCOV
526
                        "Failed to set the OpenSSL signature to sha256: " + GetOpenSSLErrorMessage()));
×
527
        }
528

529
        vector<uint8_t> signature {};
530

531
        // Set the needed signature buffer length
532
        size_t digestlength = MENDER_DIGEST_SHA256_LENGTH, siglength;
533
        if (EVP_PKEY_sign(pkey_signer_ctx.get(), nullptr, &siglength, digest.data(), digestlength)
8✔
534
                <= 0) {
UNCOV
535
                return expected::unexpected(MakeError(
×
UNCOV
536
                        SetupError, "Failed to get the signature buffer length: " + GetOpenSSLErrorMessage()));
×
537
        }
538
        signature.resize(siglength);
8✔
539

540
        if (EVP_PKEY_sign(
8✔
541
                        pkey_signer_ctx.get(), signature.data(), &siglength, digest.data(), digestlength)
542
                <= 0) {
UNCOV
543
                return expected::unexpected(
×
544
                        MakeError(SetupError, "Failed to sign the digest: " + GetOpenSSLErrorMessage()));
×
545
        }
546

547
        // The signature may in some cases be shorter than the previously allocated
548
        // length (which is the max)
549
        signature.resize(siglength);
8✔
550

551
        return signature;
8✔
552
}
553

554
expected::ExpectedBytes SignData(const Args &args, const vector<uint8_t> &raw_data) {
11✔
555
        auto exp_private_key = PrivateKey::Load(args);
11✔
556
        if (!exp_private_key) {
11✔
557
                return expected::unexpected(exp_private_key.error());
2✔
558
        }
559

560
        log::Info("Signing with: " + args.private_key_path);
10✔
561

562
        auto key_type = EVP_PKEY_base_id(exp_private_key.value().Get());
10✔
563

564
        // ED25519 signatures need to be handled independently, because of how the
565
        // signature scheme is designed.
566
        if (key_type == EVP_PKEY_ED25519) {
10✔
567
                return SignED25519(exp_private_key.value().Get(), raw_data);
2✔
568
        }
569

570
        auto exp_shasum = mender::sha::Shasum(raw_data);
8✔
571
        if (!exp_shasum) {
8✔
UNCOV
572
                return expected::unexpected(exp_shasum.error());
×
573
        }
574
        auto digest = exp_shasum.value(); /* The shasummed data = digest in crypto world */
8✔
575
        log::Debug("Shasum is: " + digest.String());
16✔
576

577
        return SignGeneric(exp_private_key.value(), digest);
16✔
578
}
579

580
expected::ExpectedString Sign(const Args &args, const vector<uint8_t> &raw_data) {
11✔
581
        auto exp_signed_data = SignData(args, raw_data);
11✔
582
        if (!exp_signed_data) {
11✔
583
                return expected::unexpected(exp_signed_data.error());
2✔
584
        }
585
        vector<uint8_t> signature = exp_signed_data.value();
10✔
586

587
        return EncodeBase64(signature);
20✔
588
}
589

590
const size_t mender_decode_buf_size = 256;
591
const size_t ecdsa256keySize = 32;
592

593
// Try and decode the keys from pure binary, assuming that the points on the
594
// curve (r,s), have been concatenated together (r || s), and simply dumped to
595
// binary. Which is what we did in the `mender-artifact` tool.
596
// (See MEN-1740) for some insight into previous issues, and the chosen fix.
597
static expected::ExpectedBytes TryASN1EncodeMenderCustomBinaryECFormat(
2✔
598
        const vector<uint8_t> &signature,
599
        const mender::sha::SHA &shasum,
600
        std::function<BIGNUM *(const unsigned char *signature, int length, BIGNUM *_unused)>
601
                BinaryDecoderFn) {
602
        // Verify that the marshalled keys match our expectation
603
        const size_t assumed_signature_size {2 * ecdsa256keySize};
604
        if (signature.size() > assumed_signature_size) {
2✔
605
                return expected::unexpected(MakeError(
×
606
                        SetupError,
UNCOV
607
                        "Unexpected size of the signature for ECDSA. Expected 2*" + to_string(ecdsa256keySize)
×
UNCOV
608
                                + " bytes. Got: " + to_string(signature.size())));
×
609
        }
610
        auto ecSig = unique_ptr<ECDSA_SIG, void (*)(ECDSA_SIG *)>(ECDSA_SIG_new(), ECDSA_SIG_free);
4✔
611
        if (ecSig == nullptr) {
2✔
UNCOV
612
                return expected::unexpected(MakeError(
×
613
                        SetupError,
614
                        "Failed to allocate the structure for the ECDSA signature: "
UNCOV
615
                                + GetOpenSSLErrorMessage()));
×
616
        }
617

618
        auto r = unique_ptr<BIGNUM, void (*)(BIGNUM *)>(
619
                BinaryDecoderFn(signature.data(), ecdsa256keySize, nullptr /* allocate new memory for r */),
620
                bn_free);
4✔
621
        if (r == nullptr) {
2✔
UNCOV
622
                return expected::unexpected(MakeError(
×
623
                        SetupError,
624
                        "Failed to extract the r(andom) part from the ECDSA signature in the binary representation: "
UNCOV
625
                                + GetOpenSSLErrorMessage()));
×
626
        }
627
        auto s = unique_ptr<BIGNUM, void (*)(BIGNUM *)>(
628
                BinaryDecoderFn(
629
                        signature.data() + ecdsa256keySize,
630
                        ecdsa256keySize,
631
                        nullptr /* allocate new memory for s */),
632
                bn_free);
4✔
633
        if (s == nullptr) {
2✔
634
                return expected::unexpected(MakeError(
×
635
                        SetupError,
636
                        "Failed to extract the s(ignature) part from the ECDSA signature in the binary representation: "
UNCOV
637
                                + GetOpenSSLErrorMessage()));
×
638
        }
639

640
        // Set the r&s values in the SIG struct
641
        // r & s now owned by ecSig
642
        int ret {ECDSA_SIG_set0(ecSig.get(), r.get(), s.get())};
2✔
643
        if (ret != OPENSSL_SUCCESS) {
2✔
UNCOV
644
                return expected::unexpected(MakeError(
×
645
                        SetupError,
646
                        "Failed to set the signature parts in the ECDSA structure: "
UNCOV
647
                                + GetOpenSSLErrorMessage()));
×
648
        }
649
        r.release();
650
        s.release();
651

652
        /* Allocate some array guaranteed to hold the DER-encoded structure */
653
        vector<uint8_t> der_encoded_byte_array(mender_decode_buf_size);
2✔
654
        unsigned char *arr_p = &der_encoded_byte_array[0];
2✔
655
        int len = i2d_ECDSA_SIG(ecSig.get(), &arr_p);
2✔
656
        if (len < 0) {
2✔
UNCOV
657
                return expected::unexpected(MakeError(
×
658
                        SetupError,
659
                        "Failed to set the signature parts in the ECDSA structure: "
UNCOV
660
                                + GetOpenSSLErrorMessage()));
×
661
        }
662
        /* Resize to the actual size of the DER-encoded signature */
663
        der_encoded_byte_array.resize(len);
2✔
664

665
        return der_encoded_byte_array;
2✔
666
}
667

668

669
expected::ExpectedBool VerifySignData(
670
        const string &public_key_path,
671
        const mender::sha::SHA &shasum,
672
        const vector<uint8_t> &signature);
673

674
static expected::ExpectedBool VerifyECDSASignData(
2✔
675
        const string &public_key_path,
676
        const mender::sha::SHA &shasum,
677
        const vector<uint8_t> &signature) {
678
        expected::ExpectedBytes exp_der_encoded_signature =
679
                TryASN1EncodeMenderCustomBinaryECFormat(signature, shasum, BN_bin2bn)
4✔
UNCOV
680
                        .or_else([&signature, &shasum](error::Error big_endian_error) {
×
UNCOV
681
                                log::Debug(
×
682
                                        "Failed to decode the signature binary blob from our custom binary format assuming the big-endian encoding, error: "
UNCOV
683
                                        + big_endian_error.String()
×
684
                                        + " falling back and trying anew assuming it is little-endian encoded: ");
×
UNCOV
685
                                return TryASN1EncodeMenderCustomBinaryECFormat(signature, shasum, BN_lebin2bn);
×
686
                        });
2✔
687
        if (!exp_der_encoded_signature) {
2✔
UNCOV
688
                return expected::unexpected(
×
UNCOV
689
                        MakeError(VerificationError, exp_der_encoded_signature.error().message));
×
690
        }
691

692
        vector<uint8_t> der_encoded_signature = exp_der_encoded_signature.value();
2✔
693

694
        return VerifySignData(public_key_path, shasum, der_encoded_signature);
2✔
695
}
696

697
static bool OpenSSLSignatureVerificationError(int a) {
698
        /*
699
         * The signature check errored. This is different from the signature being
700
         * wrong. We simply were not able to perform the check in this instance.
701
         * Therefore, we fall back to trying the custom marshalled binary ECDSA
702
         * signature, which we have been using in Mender.
703
         */
704
        return a < 0;
705
}
706

707
expected::ExpectedBool VerifySignData(
16✔
708
        const string &public_key_path,
709
        const mender::sha::SHA &shasum,
710
        const vector<uint8_t> &signature) {
711
        auto bio_key =
712
                unique_ptr<BIO, void (*)(BIO *)>(BIO_new_file(public_key_path.c_str(), "r"), bio_free_func);
32✔
713
        if (bio_key == nullptr) {
16✔
714
                return expected::unexpected(MakeError(
3✔
715
                        SetupError,
716
                        "Failed to open the public key file from (" + public_key_path
6✔
717
                                + "):" + GetOpenSSLErrorMessage()));
15✔
718
        }
719

720
        auto pkey = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
721
                PEM_read_bio_PUBKEY(bio_key.get(), nullptr, nullptr, nullptr), pkey_free_func);
26✔
722
        if (pkey == nullptr) {
13✔
723
                return expected::unexpected(MakeError(
2✔
724
                        SetupError,
725
                        "Failed to load the public key from(" + public_key_path
4✔
726
                                + "): " + GetOpenSSLErrorMessage()));
10✔
727
        }
728

729
        auto pkey_signer_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
730
                EVP_PKEY_CTX_new(pkey.get(), nullptr), pkey_ctx_free_func);
22✔
731

732
        auto ret = EVP_PKEY_verify_init(pkey_signer_ctx.get());
11✔
733
        if (ret <= 0) {
11✔
UNCOV
734
                return expected::unexpected(MakeError(
×
UNCOV
735
                        SetupError, "Failed to initialize the OpenSSL signer: " + GetOpenSSLErrorMessage()));
×
736
        }
737
        ret = EVP_PKEY_CTX_set_signature_md(pkey_signer_ctx.get(), EVP_sha256());
11✔
738
        if (ret <= 0) {
11✔
UNCOV
739
                return expected::unexpected(MakeError(
×
740
                        SetupError,
UNCOV
741
                        "Failed to set the OpenSSL signature to sha256: " + GetOpenSSLErrorMessage()));
×
742
        }
743

744
        // verify signature
745
        ret = EVP_PKEY_verify(
11✔
746
                pkey_signer_ctx.get(), signature.data(), signature.size(), shasum.data(), shasum.size());
747
        if (OpenSSLSignatureVerificationError(ret)) {
11✔
748
                log::Debug(
2✔
749
                        "Failed to verify the signature with the supported OpenSSL binary formats. Falling back to the custom Mender encoded binary format for ECDSA signatures: "
750
                        + GetOpenSSLErrorMessage());
4✔
751
                return VerifyECDSASignData(public_key_path, shasum, signature);
2✔
752
        }
753
        if (ret == OPENSSL_SUCCESS) {
9✔
754
                return true;
755
        }
756
        /* This is the case where ret == 0. The signature is simply wrong */
757
        return false;
758
}
759

760
expected::ExpectedBool VerifySign(
14✔
761
        const string &public_key_path, const mender::sha::SHA &shasum, const string &signature) {
762
        // signature: decode base64
763
        auto exp_decoded_signature = DecodeBase64(signature);
28✔
764
        if (!exp_decoded_signature) {
14✔
UNCOV
765
                return expected::unexpected(exp_decoded_signature.error());
×
766
        }
767
        auto decoded_signature = exp_decoded_signature.value();
14✔
768

769
        return VerifySignData(public_key_path, shasum, decoded_signature);
14✔
770
}
771

772
error::Error PrivateKey::SaveToPEM(const string &private_key_path) {
6✔
773
        auto bio_key = unique_ptr<BIO, void (*)(BIO *)>(
774
                BIO_new_file(private_key_path.c_str(), "w"), bio_free_func);
12✔
775
        if (bio_key == nullptr) {
6✔
776
                return MakeError(
777
                        SetupError,
778
                        "Failed to open the private key file (" + private_key_path
2✔
779
                                + "): " + GetOpenSSLErrorMessage());
4✔
780
        }
781

782
        auto ret =
783
                PEM_write_bio_PrivateKey(bio_key.get(), key.get(), nullptr, nullptr, 0, nullptr, nullptr);
5✔
784
        if (ret != OPENSSL_SUCCESS) {
5✔
785
                return MakeError(
786
                        SetupError,
UNCOV
787
                        "Failed to save the private key to file (" + private_key_path
×
UNCOV
788
                                + "): " + GetOpenSSLErrorMessage());
×
789
        }
790

791
        return error::NoError;
5✔
792
}
793

794
} // namespace crypto
795
} // namespace common
796
} // 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