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

mendersoftware / mender / 1057637229

01 Nov 2023 01:17PM UTC coverage: 79.933% (-0.3%) from 80.207%
1057637229

push

gitlab-ci

oleorhagen
chore: Alias mender-authd.service -> mender-client.service

This aliases the systemd.service for `mender-authd` to `mender-client`, so that
other existing third-party services relying on it will still work transparently.

Ticket: MEN-6812

Signed-off-by: Ole Petter <ole.orhagen@northern.tech>

6899 of 8631 relevant lines covered (79.93%)

9322.97 hits per line

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

64.36
/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
#ifdef MENDER_CRYPTO_OPENSSL_LEGACY
28
#include <openssl/engine.h>
29
#else
30
#include <openssl/provider.h>
31
#include <openssl/store.h>
32
#endif // MENDER_CRYPTO_OPENSSL_LEGACY
33

34
#include <openssl/evp.h>
35
#include <openssl/pem.h>
36
#include <openssl/rsa.h>
37

38
#include <common/io.hpp>
39
#include <common/error.hpp>
40
#include <common/expected.hpp>
41
#include <common/common.hpp>
42

43
#include <artifact/sha/sha.hpp>
44

45

46
namespace mender {
47
namespace common {
48
namespace crypto {
49

50
const size_t MENDER_DIGEST_SHA256_LENGTH = 32;
51

52
const size_t OPENSSL_SUCCESS = 1;
53

54
using namespace std;
55

56
namespace error = mender::common::error;
57
namespace io = mender::common::io;
58

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

65
#ifdef MENDER_CRYPTO_OPENSSL_LEGACY
66
class OpenSSLResourceHandle {
×
67
public:
68
        EnginePtr engine;
69
};
70
#else
71
class OpenSSLResourceHandle {
72
public:
73
        ProviderPtr default_provider_;
74
        ProviderPtr hsm_provider_;
75
};
76
#endif // MENDER_CRYPTO_OPENSSL_LEGACY
77
auto resource_handle_free_func = [](OpenSSLResourceHandle *h) {
×
78
        if (h) {
×
79
                delete h;
×
80
        }
81
};
×
82

83
auto pkey_ctx_free_func = [](EVP_PKEY_CTX *ctx) {
28✔
84
        if (ctx) {
28✔
85
                EVP_PKEY_CTX_free(ctx);
28✔
86
        }
87
};
28✔
88
auto pkey_free_func = [](EVP_PKEY *key) {
44✔
89
        if (key) {
44✔
90
                EVP_PKEY_free(key);
44✔
91
        }
92
};
44✔
93
auto bio_free_func = [](BIO *bio) {
47✔
94
        if (bio) {
47✔
95
                BIO_free(bio);
47✔
96
        }
97
};
47✔
98
auto bio_free_all_func = [](BIO *bio) {
7✔
99
        if (bio) {
7✔
100
                BIO_free_all(bio);
7✔
101
        }
102
};
7✔
103
#ifdef MENDER_CRYPTO_OPENSSL_LEGACY
104
auto bn_free = [](BIGNUM *bn) {
×
105
        if (bn) {
×
106
                BN_free(bn);
×
107
        }
108
};
×
109
auto engine_free_func = [](ENGINE *e) {
×
110
        if (e) {
×
111
                ENGINE_free(e);
×
112
        }
113
};
×
114
#endif
115

116
// NOTE: GetOpenSSLErrorMessage should be called upon all OpenSSL errors, as
117
// the errors are queued, and if not harvested, the FIFO structure of the
118
// queue will mean that if you just get one, you might actually get the wrong
119
// one.
120
string GetOpenSSLErrorMessage() {
18✔
121
        const auto sysErrorCode = errno;
18✔
122
        auto sslErrorCode = ERR_get_error();
18✔
123

124
        std::string errorDescription {};
125
        while (sslErrorCode != 0) {
59✔
126
                if (!errorDescription.empty()) {
41✔
127
                        errorDescription += '\n';
128
                }
129
                errorDescription += ERR_error_string(sslErrorCode, nullptr);
41✔
130
                sslErrorCode = ERR_get_error();
41✔
131
        }
132
        if (sysErrorCode != 0) {
18✔
133
                if (!errorDescription.empty()) {
16✔
134
                        errorDescription += '\n';
135
                }
136
                errorDescription += "System error, code=" + std::to_string(sysErrorCode);
32✔
137
                errorDescription += ", ";
16✔
138
                errorDescription += strerror(sysErrorCode);
16✔
139
        }
140
        return errorDescription;
18✔
141
}
142

143
ExpectedPrivateKey PrivateKey::LoadFromPEM(
35✔
144
        const string &private_key_path, const string &passphrase) {
145
        log::Trace("Loading private key from file: " + private_key_path);
70✔
146
        auto private_bio_key = unique_ptr<BIO, void (*)(BIO *)>(
147
                BIO_new_file(private_key_path.c_str(), "r"), bio_free_func);
70✔
148
        if (private_bio_key == nullptr) {
35✔
149
                return expected::unexpected(MakeError(
6✔
150
                        SetupError,
151
                        "Failed to open the private key file " + private_key_path + ": "
12✔
152
                                + GetOpenSSLErrorMessage()));
30✔
153
        }
154

155
        vector<char> chars(passphrase.begin(), passphrase.end());
29✔
156
        chars.push_back('\0');
29✔
157
        char *c_str = chars.data();
158

159
        // We need our own custom callback routine, as the default one will prompt
160
        // for a passphrase.
161
        auto callback = [](char *buf, int size, int rwflag, void *u) {
3✔
162
                // We'll only use this callback for reading passphrases, not for
163
                // writing them.
164
                assert(rwflag == 0);
165

166
                if (u == nullptr) {
3✔
167
                        return 0;
168
                }
169

170
                // NB: buf is not expected to be null terminated.
171
                char *const pass = static_cast<char *>(u);
172
                strncpy(buf, pass, size);
3✔
173

174
                const int len = static_cast<int>(strlen(pass));
3✔
175
                return (len < size) ? len : size;
176
        };
177

178
        auto private_key = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
179
                PEM_read_bio_PrivateKey(private_bio_key.get(), nullptr, callback, c_str), pkey_free_func);
58✔
180
        if (private_key == nullptr) {
29✔
181
                return expected::unexpected(MakeError(
4✔
182
                        SetupError,
183
                        "Failed to load the key: " + private_key_path + " " + GetOpenSSLErrorMessage()));
12✔
184
        }
185

186
        return PrivateKey(std::move(private_key));
25✔
187
}
188

189
#ifdef MENDER_CRYPTO_OPENSSL_LEGACY
190
ExpectedPrivateKey PrivateKey::LoadFromHSM(const Args &args) {
×
191
        log::Trace("Loading the private key from HSM");
×
192

193
        ENGINE_load_builtin_engines();
×
194
        auto engine = EnginePtr(ENGINE_by_id(args.ssl_engine.c_str()), engine_free_func);
×
195

196
        if (engine == nullptr) {
×
197
                return expected::unexpected(MakeError(
×
198
                        SetupError,
199
                        "Failed to get the " + args.ssl_engine
×
200
                                + " engine. No engine with the ID found: " + GetOpenSSLErrorMessage()));
×
201
        }
202
        log::Debug("Loaded the HSM engine successfully!");
×
203

204
        int res = ENGINE_init(engine.get());
×
205
        if (not res) {
×
206
                return expected::unexpected(MakeError(
×
207
                        SetupError,
208
                        "Failed to initialise the hardware security module (HSM): "
209
                                + GetOpenSSLErrorMessage()));
×
210
        }
211
        log::Debug("Successfully initialised the HSM engine");
×
212

213
        auto private_key = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
214
                ENGINE_load_private_key(
215
                        engine.get(),
216
                        args.private_key_path.c_str(),
217
                        (UI_METHOD *) nullptr,
218
                        nullptr /*callback_data */),
219
                pkey_free_func);
×
220
        if (private_key == nullptr) {
×
221
                return expected::unexpected(MakeError(
×
222
                        SetupError,
223
                        "Failed to load the private key from the hardware security module: "
224
                                + GetOpenSSLErrorMessage()));
×
225
        }
226
        log::Debug("Successfully loaded the private key from the HSM Engine: " + args.ssl_engine);
×
227

228
        auto handle = unique_ptr<OpenSSLResourceHandle, void (*)(OpenSSLResourceHandle *)>(
229
                new OpenSSLResourceHandle {std::move(engine)}, resource_handle_free_func);
×
230
        return PrivateKey(std::move(private_key), std::move(handle));
×
231
}
232
#endif // MENDER_CRYPTO_OPENSSL_LEGACY
233

234
#ifndef MENDER_CRYPTO_OPENSSL_LEGACY
235
ExpectedPrivateKey PrivateKey::LoadFromHSM(const Args &args) {
236
        log::Debug("Loading the private key from HSM");
237

238
        auto default_provider =
239
                ProviderPtr(OSSL_PROVIDER_load(nullptr, "default"), OSSL_PROVIDER_unload);
240
        if (default_provider == nullptr) {
241
                return expected::unexpected(
242
                        MakeError(SetupError, "default provider load error: " + GetOpenSSLErrorMessage()));
243
        }
244

245
        auto hsm_provider =
246
                ProviderPtr(OSSL_PROVIDER_load(nullptr, args.ssl_engine.c_str()), OSSL_PROVIDER_unload);
247
        if (hsm_provider == nullptr) {
248
                return expected::unexpected(MakeError(
249
                        SetupError, args.ssl_engine + " provider load error: " + GetOpenSSLErrorMessage()));
250
        }
251

252
        int ret {OSSL_PROVIDER_available(nullptr, args.ssl_engine.c_str())};
253
        if (ret != OPENSSL_SUCCESS) {
254
                return expected::unexpected(MakeError(
255
                        SetupError, args.ssl_engine + " provider not available: " + GetOpenSSLErrorMessage()));
256
        }
257

258
        auto ctx = unique_ptr<OSSL_STORE_CTX, int (*)(OSSL_STORE_CTX *)>(
259
                OSSL_STORE_open(args.private_key_path.c_str(), nullptr, nullptr, nullptr, nullptr),
260
                OSSL_STORE_close);
261

262
        if (ctx == nullptr) {
263
                return expected::unexpected(MakeError(
264
                        SetupError,
265
                        "OSSL_STORE_OPEN: Failed to load the private key from the hardware security module: "
266
                                + GetOpenSSLErrorMessage()));
267
        }
268

269
        // Go through all objects in the context till we find the first private key
270
        while (not OSSL_STORE_eof(ctx.get())) {
271
                OSSL_STORE_INFO *info = OSSL_STORE_load(ctx.get());
272

273
                if (info == nullptr) {
274
                        log::Error(
275
                                "Failed to load the store info from the hardware security module: trying the next object in the context: "
276
                                + GetOpenSSLErrorMessage());
277
                        continue;
278
                }
279

280
                const int type_info {OSSL_STORE_INFO_get_type(info)};
281
                switch (type_info) {
282
                case OSSL_STORE_INFO_PKEY: {
283
                        // NOTE: get1 creates a duplicate of the pkey from the info, which can be
284
                        // used after the info ctx is destroyed
285
                        auto private_key = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
286
                                OSSL_STORE_INFO_get1_PKEY(info), pkey_free_func);
287
                        if (private_key == nullptr) {
288
                                return expected::unexpected(MakeError(
289
                                        SetupError,
290
                                        "Failed to load the private key from the hardware security module: "
291
                                                + GetOpenSSLErrorMessage()));
292
                        }
293

294
                        // OpenSSLResourceHandle handle {std::move(default_provider), std::move(hsm_provider)};
295
                        auto handle = unique_ptr<OpenSSLResourceHandle, void (*)(OpenSSLResourceHandle *)>(
296
                                new OpenSSLResourceHandle {std::move(default_provider), std::move(hsm_provider)},
297
                                resource_handle_free_func);
298
                        return PrivateKey(std::move(private_key), std::move(handle));
299
                }
300
                default:
301
                        const string info_type_string = OSSL_STORE_INFO_type_string(type_info);
302
                        log::Debug("Unhandled OpenSSL type: expected PrivateKey, got: " + info_type_string);
303
                        continue;
304
                }
305
        }
306

307
        return expected::unexpected(MakeError(
308
                SetupError,
309
                "Failed to load the private key from the hardware security module: "
310
                        + GetOpenSSLErrorMessage()));
311
}
312
#endif // ndef MENDER_CRYPTO_OPENSSL_LEGACY
313

314
ExpectedPrivateKey PrivateKey::Load(const Args &args) {
27✔
315
        log::Trace("Loading private key");
54✔
316
        if (args.ssl_engine != "") {
27✔
317
                return LoadFromHSM(args);
×
318
        }
319
        return LoadFromPEM(args.private_key_path, args.private_key_passphrase);
27✔
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) {
10✔
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 uint64_t predicted_len {4 * ((to_encode.size() + 2) / 3)};
10✔
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)};
10✔
418

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

423
        if (predicted_len != static_cast<uint64_t>(output_len)) {
10✔
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
20✔
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 uint64_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) {
8✔
464
        auto exp_private_key = PrivateKey::Load(args);
8✔
465
        if (!exp_private_key) {
8✔
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);
14✔
470

471
        if (!bio_public_key.get()) {
7✔
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());
7✔
479
        if (ret != OPENSSL_SUCCESS) {
7✔
480
                return expected::unexpected(MakeError(
×
481
                        SetupError,
482
                        "Failed to extract the public key from: (" + args.private_key_path
×
483
                                + "): OpenSSL BIO write failed: " + GetOpenSSLErrorMessage()));
×
484
        }
485

486
        int pending = BIO_ctrl_pending(bio_public_key.get());
7✔
487
        if (pending <= 0) {
7✔
488
                return expected::unexpected(MakeError(
×
489
                        SetupError,
490
                        "Failed to extract the public key from: (" + args.private_key_path
×
491
                                + "): Zero byte key unexpected: " + GetOpenSSLErrorMessage()));
×
492
        }
493

494
        vector<uint8_t> key_vector(pending);
7✔
495

496
        size_t read = BIO_read(bio_public_key.get(), key_vector.data(), pending);
7✔
497

498
        if (read == 0) {
7✔
499
                MakeError(
×
500
                        SetupError,
501
                        "Failed to extract the public key from (" + args.private_key_path
×
502
                                + "): Zero bytes read from BIO: " + GetOpenSSLErrorMessage());
×
503
        }
504

505
        return string(key_vector.begin(), key_vector.end());
14✔
506
}
507

508
expected::ExpectedBytes SignData(const Args &args, const vector<uint8_t> &digest) {
10✔
509
        auto exp_private_key = PrivateKey::Load(args);
10✔
510
        if (!exp_private_key) {
10✔
511
                return expected::unexpected(exp_private_key.error());
2✔
512
        }
513

514
        auto pkey_signer_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
515
                EVP_PKEY_CTX_new(exp_private_key.value().Get(), nullptr), pkey_ctx_free_func);
18✔
516

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

527
        vector<uint8_t> signature {};
528

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

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

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

549
        return signature;
9✔
550
}
551

552
expected::ExpectedString Sign(const Args &args, const mender::sha::SHA &shasum) {
10✔
553
        auto exp_signed_data = SignData(args, shasum);
20✔
554
        if (!exp_signed_data) {
10✔
555
                return expected::unexpected(exp_signed_data.error());
2✔
556
        }
557
        vector<uint8_t> signature = exp_signed_data.value();
9✔
558

559
        return EncodeBase64(signature);
18✔
560
}
561

562
expected::ExpectedString SignRawData(const Args &args, const vector<uint8_t> &raw_data) {
9✔
563
        auto exp_shasum = mender::sha::Shasum(raw_data);
9✔
564

565
        if (!exp_shasum) {
9✔
566
                return expected::unexpected(exp_shasum.error());
×
567
        }
568
        auto shasum = exp_shasum.value();
9✔
569
        log::Debug("Shasum is: " + shasum.String());
18✔
570

571
        return Sign(args, shasum);
9✔
572
}
573

574
const size_t mender_decode_buf_size = 256;
575
const size_t ecdsa256keySize = 32;
576

577
// Try and decode the keys from pure binary, assuming that the points on the
578
// curve (r,s), have been concatenated together (r || s), and simply dumped to
579
// binary. Which is what we did in the `mender-artifact` tool.
580
// (See MEN-1740) for some insight into previous issues, and the chosen fix.
581
static expected::ExpectedBytes TryASN1EncodeMenderCustomBinaryECFormat(
2✔
582
        const vector<uint8_t> &signature,
583
        const mender::sha::SHA &shasum,
584
        std::function<BIGNUM *(const unsigned char *signature, int length, BIGNUM *_unused)>
585
                BinaryDecoderFn) {
586
        // Verify that the marshalled keys match our expectation
587
        const size_t assumed_signature_size {2 * ecdsa256keySize};
588
        if (signature.size() > assumed_signature_size) {
2✔
589
                return expected::unexpected(MakeError(
×
590
                        SetupError,
591
                        "Unexpected size of the signature for ECDSA. Expected 2*" + to_string(ecdsa256keySize)
×
592
                                + " bytes. Got: " + to_string(signature.size())));
×
593
        }
594
        auto ecSig = unique_ptr<ECDSA_SIG, void (*)(ECDSA_SIG *)>(ECDSA_SIG_new(), ECDSA_SIG_free);
4✔
595
        if (ecSig == nullptr) {
2✔
596
                return expected::unexpected(MakeError(
×
597
                        SetupError,
598
                        "Failed to allocate the structure for the ECDSA signature: "
599
                                + GetOpenSSLErrorMessage()));
×
600
        }
601

602
        auto r = unique_ptr<BIGNUM, void (*)(BIGNUM *)>(
603
                BinaryDecoderFn(signature.data(), ecdsa256keySize, nullptr /* allocate new memory for r */),
604
                BN_free);
4✔
605
        if (r == nullptr) {
2✔
606
                return expected::unexpected(MakeError(
×
607
                        SetupError,
608
                        "Failed to extract the r(andom) part from the ECDSA signature in the binary representation: "
609
                                + GetOpenSSLErrorMessage()));
×
610
        }
611
        auto s = unique_ptr<BIGNUM, void (*)(BIGNUM *)>(
612
                BinaryDecoderFn(
613
                        signature.data() + ecdsa256keySize,
614
                        ecdsa256keySize,
615
                        nullptr /* allocate new memory for s */),
616
                BN_free);
4✔
617
        if (s == nullptr) {
2✔
618
                return expected::unexpected(MakeError(
×
619
                        SetupError,
620
                        "Failed to extract the s(ignature) part from the ECDSA signature in the binary representation: "
621
                                + GetOpenSSLErrorMessage()));
×
622
        }
623

624
        // Set the r&s values in the SIG struct
625
        // r & s now owned by ecSig
626
        int ret {ECDSA_SIG_set0(ecSig.get(), r.get(), s.get())};
2✔
627
        if (ret != OPENSSL_SUCCESS) {
2✔
628
                return expected::unexpected(MakeError(
×
629
                        SetupError,
630
                        "Failed to set the signature parts in the ECDSA structure: "
631
                                + GetOpenSSLErrorMessage()));
×
632
        }
633
        r.release();
634
        s.release();
635

636
        /* Allocate some array guaranteed to hold the DER-encoded structure */
637
        vector<uint8_t> der_encoded_byte_array(mender_decode_buf_size);
2✔
638
        unsigned char *arr_p = &der_encoded_byte_array[0];
2✔
639
        int len = i2d_ECDSA_SIG(ecSig.get(), &arr_p);
2✔
640
        if (len < 0) {
2✔
641
                return expected::unexpected(MakeError(
×
642
                        SetupError,
643
                        "Failed to set the signature parts in the ECDSA structure: "
644
                                + GetOpenSSLErrorMessage()));
×
645
        }
646
        /* Resize to the actual size of the DER-encoded signature */
647
        der_encoded_byte_array.resize(len);
2✔
648

649
        return der_encoded_byte_array;
2✔
650
}
651

652

653
expected::ExpectedBool VerifySignData(
654
        const string &public_key_path,
655
        const mender::sha::SHA &shasum,
656
        const vector<uint8_t> &signature);
657

658
static expected::ExpectedBool VerifyECDSASignData(
2✔
659
        const string &public_key_path,
660
        const mender::sha::SHA &shasum,
661
        const vector<uint8_t> &signature) {
662
        expected::ExpectedBytes exp_der_encoded_signature =
663
                TryASN1EncodeMenderCustomBinaryECFormat(signature, shasum, BN_bin2bn)
4✔
664
                        .or_else([&signature, &shasum](error::Error big_endian_error) {
×
665
                                log::Debug(
×
666
                                        "Failed to decode the signature binary blob from our custom binary format assuming the big-endian encoding, error: "
667
                                        + big_endian_error.String()
×
668
                                        + " falling back and trying anew assuming it is little-endian encoded: ");
×
669
                                return TryASN1EncodeMenderCustomBinaryECFormat(signature, shasum, BN_lebin2bn);
×
670
                        });
2✔
671
        if (!exp_der_encoded_signature) {
2✔
672
                return expected::unexpected(
×
673
                        MakeError(VerificationError, exp_der_encoded_signature.error().message));
×
674
        }
675

676
        vector<uint8_t> der_encoded_signature = exp_der_encoded_signature.value();
2✔
677

678
        return VerifySignData(public_key_path, shasum, der_encoded_signature);
2✔
679
}
680

681
static bool OpenSSLSignatureVerificationError(int a) {
682
        /*
683
         * The signature check errored. This is different from the signature being
684
         * wrong. We simply were not able to perform the check in this instance.
685
         * Therefore, we fall back to trying the custom marshalled binary ECDSA
686
         * signature, which we have been using in Mender.
687
         */
688
        return a < 0;
689
}
690

691
expected::ExpectedBool VerifySignData(
16✔
692
        const string &public_key_path,
693
        const mender::sha::SHA &shasum,
694
        const vector<uint8_t> &signature) {
695
        auto bio_key =
696
                unique_ptr<BIO, void (*)(BIO *)>(BIO_new_file(public_key_path.c_str(), "r"), bio_free_func);
32✔
697
        if (bio_key == nullptr) {
16✔
698
                return expected::unexpected(MakeError(
3✔
699
                        SetupError,
700
                        "Failed to open the public key file from (" + public_key_path
6✔
701
                                + "):" + GetOpenSSLErrorMessage()));
15✔
702
        }
703

704
        auto pkey = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
705
                PEM_read_bio_PUBKEY(bio_key.get(), nullptr, nullptr, nullptr), pkey_free_func);
26✔
706
        if (pkey == nullptr) {
13✔
707
                return expected::unexpected(MakeError(
2✔
708
                        SetupError,
709
                        "Failed to load the public key from(" + public_key_path
4✔
710
                                + "): " + GetOpenSSLErrorMessage()));
10✔
711
        }
712

713
        auto pkey_signer_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
714
                EVP_PKEY_CTX_new(pkey.get(), nullptr), pkey_ctx_free_func);
22✔
715

716
        auto ret = EVP_PKEY_verify_init(pkey_signer_ctx.get());
11✔
717
        if (ret <= 0) {
11✔
718
                return expected::unexpected(MakeError(
×
719
                        SetupError, "Failed to initialize the OpenSSL signer: " + GetOpenSSLErrorMessage()));
×
720
        }
721
        ret = EVP_PKEY_CTX_set_signature_md(pkey_signer_ctx.get(), EVP_sha256());
11✔
722
        if (ret <= 0) {
11✔
723
                return expected::unexpected(MakeError(
×
724
                        SetupError,
725
                        "Failed to set the OpenSSL signature to sha256: " + GetOpenSSLErrorMessage()));
×
726
        }
727

728
        // verify signature
729
        ret = EVP_PKEY_verify(
11✔
730
                pkey_signer_ctx.get(), signature.data(), signature.size(), shasum.data(), shasum.size());
731
        if (OpenSSLSignatureVerificationError(ret)) {
11✔
732
                log::Debug(
2✔
733
                        "Failed to verify the signature with the supported OpenSSL binary formats. Falling back to the custom Mender encoded binary format for ECDSA signatures: "
734
                        + GetOpenSSLErrorMessage());
4✔
735
                return VerifyECDSASignData(public_key_path, shasum, signature);
2✔
736
        }
737
        if (ret == OPENSSL_SUCCESS) {
9✔
738
                return true;
739
        }
740
        /* This is the case where ret == 0. The signature is simply wrong */
741
        return false;
742
}
743

744
expected::ExpectedBool VerifySign(
14✔
745
        const string &public_key_path, const mender::sha::SHA &shasum, const string &signature) {
746
        // signature: decode base64
747
        auto exp_decoded_signature = DecodeBase64(signature);
28✔
748
        if (!exp_decoded_signature) {
14✔
749
                return expected::unexpected(exp_decoded_signature.error());
×
750
        }
751
        auto decoded_signature = exp_decoded_signature.value();
14✔
752

753
        return VerifySignData(public_key_path, shasum, decoded_signature);
14✔
754
}
755

756
error::Error PrivateKey::SaveToPEM(const string &private_key_path) {
6✔
757
        auto bio_key = unique_ptr<BIO, void (*)(BIO *)>(
758
                BIO_new_file(private_key_path.c_str(), "w"), bio_free_func);
12✔
759
        if (bio_key == nullptr) {
6✔
760
                return MakeError(
761
                        SetupError,
762
                        "Failed to open the private key file (" + private_key_path
2✔
763
                                + "): " + GetOpenSSLErrorMessage());
4✔
764
        }
765

766
        // PEM_write_bio_PrivateKey_traditional will use the key-specific PKCS1
767
        // format if one is available for that key type, otherwise it will encode
768
        // to a PKCS8 key.
769
        auto ret = PEM_write_bio_PrivateKey_traditional(
5✔
770
                bio_key.get(), key.get(), nullptr, nullptr, 0, nullptr, nullptr);
771
        if (ret != OPENSSL_SUCCESS) {
5✔
772
                return MakeError(
773
                        SetupError,
774
                        "Failed to save the private key to file (" + private_key_path
×
775
                                + "): " + GetOpenSSLErrorMessage());
×
776
        }
777

778
        return error::NoError;
5✔
779
}
780

781
} // namespace crypto
782
} // namespace common
783
} // 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