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

mendersoftware / mender / 1478658512

02 Oct 2024 12:15PM UTC coverage: 76.346% (+0.02%) from 76.326%
1478658512

push

gitlab-ci

danielskinstad
feat: change generated key from RSA to ED25519

Ticket: MEN-7534
Changelog: Title

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

6 of 6 new or added lines in 3 files covered. (100.0%)

5 existing lines in 2 files now uncovered.

7359 of 9639 relevant lines covered (76.35%)

11337.26 hits per line

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

63.91
/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
#ifndef MENDER_CRYPTO_OPENSSL_LEGACY
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/conf.h>
36
#include <openssl/pem.h>
37
#include <openssl/rsa.h>
38

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

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

46

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

51
const size_t MENDER_DIGEST_SHA256_LENGTH = 32;
52

53
const size_t OPENSSL_SUCCESS = 1;
54

55
using namespace std;
56

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

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

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

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

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

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

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

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

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

123

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

284
ExpectedPrivateKey PrivateKey::Load(const Args &args) {
37✔
285
        // Load OpenSSL config
286
        if ((CONF_modules_load_file(nullptr, nullptr, 0) != OPENSSL_SUCCESS)) {
37✔
287
                log::Warning("Failed to load OpenSSL configuration file: " + GetOpenSSLErrorMessage());
44✔
288
        }
289

290
        log::Trace("Loading private key");
74✔
291
        if (args.ssl_engine != "") {
37✔
292
                return LoadFromHSMEngine(args);
×
293
        }
294
        return LoadFrom(args);
37✔
295
}
296

297
ExpectedPrivateKey PrivateKey::Generate() {
7✔
298
#ifdef MENDER_CRYPTO_OPENSSL_LEGACY
299
        auto pkey_gen_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
300
                EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, nullptr), pkey_ctx_free_func);
14✔
301
#else
302
        auto pkey_gen_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
303
                EVP_PKEY_CTX_new_from_name(nullptr, "ED25519", nullptr), pkey_ctx_free_func);
304
#endif // MENDER_CRYPTO_OPENSSL_LEGACY
305

306
        int ret = EVP_PKEY_keygen_init(pkey_gen_ctx.get());
7✔
307
        if (ret != OPENSSL_SUCCESS) {
7✔
UNCOV
308
                return expected::unexpected(MakeError(
×
309
                        SetupError,
310
                        "Failed to generate a private key. Initialization failed: "
UNCOV
311
                                + GetOpenSSLErrorMessage()));
×
312
        }
313
        EVP_PKEY *pkey = nullptr;
7✔
314
#ifdef MENDER_CRYPTO_OPENSSL_LEGACY
315
        ret = EVP_PKEY_keygen(pkey_gen_ctx.get(), &pkey);
7✔
316
#else
317
        ret = EVP_PKEY_generate(pkey_gen_ctx.get(), &pkey);
318
#endif // MENDER_CRYPTO_OPENSSL_LEGACY
319
        if (ret != OPENSSL_SUCCESS) {
7✔
UNCOV
320
                return expected::unexpected(MakeError(
×
321
                        SetupError,
UNCOV
322
                        "Failed to generate a private key. Generation failed: " + GetOpenSSLErrorMessage()));
×
323
        }
324

325
        auto private_key = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(pkey, pkey_free_func);
7✔
326
        return PrivateKey(std::move(private_key));
7✔
327
}
328

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

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

340
        const int64_t output_len {
341
                EVP_EncodeBlock(buffer.data(), to_encode.data(), static_cast<int>(to_encode.size()))};
14✔
342
        assert(output_len >= 0);
343

344
        if (predicted_len != static_cast<uint64_t>(output_len)) {
14✔
345
                return expected::unexpected(
×
346
                        MakeError(Base64Error, "The predicted and the actual length differ"));
×
347
        }
348

349
        return string(buffer.begin(), buffer.end() - 1); // Remove the last zero byte
28✔
350
}
351

352
expected::ExpectedBytes DecodeBase64(string to_decode) {
15✔
353
        // Predict the len of the decoded for later verification. From man page:
354
        // For every 4 input bytes exactly 3 output bytes will be
355
        // produced. The output will be padded with 0 bits if necessary
356
        // to ensure that the output is always 3 bytes.
357
        const uint64_t predicted_len {3 * ((to_decode.size() + 3) / 4)};
15✔
358

359
        auto buffer {vector<unsigned char>(predicted_len)};
15✔
360

361
        const int64_t output_len {EVP_DecodeBlock(
15✔
362
                buffer.data(),
363
                common::ByteVectorFromString(to_decode).data(),
15✔
364
                static_cast<int>(to_decode.size()))};
15✔
365
        assert(output_len >= 0);
366

367
        if (predicted_len != static_cast<uint64_t>(output_len)) {
15✔
368
                return expected::unexpected(MakeError(
×
369
                        Base64Error,
370
                        "The predicted (" + std::to_string(predicted_len) + ") and the actual ("
×
371
                                + std::to_string(output_len) + ") length differ"));
×
372
        }
373

374
        // Subtract padding bytes. Inspired by internal OpenSSL code from:
375
        // https://github.com/openssl/openssl/blob/ff88545e02ab48a52952350c52013cf765455dd3/crypto/ct/ct_b64.c#L46
376
        for (auto it = to_decode.crbegin(); *it == '='; it++) {
23✔
377
                buffer.pop_back();
378
        }
379

380
        return buffer;
15✔
381
}
382

383

384
expected::ExpectedString ExtractPublicKey(const Args &args) {
9✔
385
        auto exp_private_key = PrivateKey::Load(args);
9✔
386
        if (!exp_private_key) {
9✔
387
                return expected::unexpected(exp_private_key.error());
2✔
388
        }
389

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

392
        if (!bio_public_key.get()) {
8✔
393
                return expected::unexpected(MakeError(
×
394
                        SetupError,
395
                        "Failed to extract the public key from the private key " + args.private_key_path
×
396
                                + "):" + GetOpenSSLErrorMessage()));
×
397
        }
398

399
        int ret = PEM_write_bio_PUBKEY(bio_public_key.get(), exp_private_key.value().Get());
8✔
400
        if (ret != OPENSSL_SUCCESS) {
8✔
401
                return expected::unexpected(MakeError(
×
402
                        SetupError,
403
                        "Failed to extract the public key from the private key (" + args.private_key_path
×
404
                                + "): OpenSSL BIO write failed: " + GetOpenSSLErrorMessage()));
×
405
        }
406

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

418

419
        auto evp_public_key = PkeyPtr(
420
                PEM_read_bio_PUBKEY(bio_public_key.get(), nullptr, nullptr, nullptr), pkey_free_func);
16✔
421

422
        if (evp_public_key == nullptr) {
8✔
423
                return expected::unexpected(MakeError(
×
424
                        SetupError,
425
                        "Failed to extract the public key from the private key " + args.private_key_path
×
426
                                + "):" + GetOpenSSLErrorMessage()));
×
427
        }
428

429
        auto bio_public_key_new =
430
                unique_ptr<BIO, void (*)(BIO *)>(BIO_new(BIO_s_mem()), bio_free_all_func);
16✔
431

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

439
        ret = PEM_write_bio_PUBKEY(bio_public_key_new.get(), evp_public_key.get());
8✔
440
        if (ret != OPENSSL_SUCCESS) {
8✔
441
                return expected::unexpected(MakeError(
×
442
                        SetupError,
443
                        "Failed to extract the public key from the private key: (" + args.private_key_path
×
444
                                + "): OpenSSL BIO write failed: " + GetOpenSSLErrorMessage()));
×
445
        }
446

447
        int pending = BIO_ctrl_pending(bio_public_key_new.get());
8✔
448
        if (pending <= 0) {
8✔
449
                return expected::unexpected(MakeError(
×
450
                        SetupError,
451
                        "Failed to extract the public key from bio ctrl: (" + args.private_key_path
×
452
                                + "): Zero byte key unexpected: " + GetOpenSSLErrorMessage()));
×
453
        }
454

455
        vector<uint8_t> key_vector(pending);
8✔
456

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

459
        if (read == 0) {
8✔
460
                MakeError(
×
461
                        SetupError,
462
                        "Failed to extract the public key from (" + args.private_key_path
×
463
                                + "): Zero bytes read from BIO: " + GetOpenSSLErrorMessage());
×
464
        }
465

466
        return string(key_vector.begin(), key_vector.end());
16✔
467
}
468

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

472
        auto md_ctx = unique_ptr<EVP_MD_CTX, void (*)(EVP_MD_CTX *)>(EVP_MD_CTX_new(), EVP_MD_CTX_free);
4✔
473
        if (md_ctx == nullptr) {
2✔
474
                return expected::unexpected(MakeError(
×
475
                        SetupError, "Failed to initialize the OpenSSL md_ctx: " + GetOpenSSLErrorMessage()));
×
476
        }
477

478
        int ret {EVP_DigestSignInit(md_ctx.get(), nullptr, nullptr, nullptr, pkey)};
2✔
479
        if (ret != OPENSSL_SUCCESS) {
2✔
480
                return expected::unexpected(MakeError(
×
481
                        SetupError, "Failed to initialize the OpenSSL signature: " + GetOpenSSLErrorMessage()));
×
482
        }
483

484
        /* Calculate the required size for the signature by passing a nullptr buffer */
485
        ret = EVP_DigestSign(md_ctx.get(), nullptr, &sig_len, raw_data.data(), raw_data.size());
2✔
486
        if (ret != OPENSSL_SUCCESS) {
2✔
487
                return expected::unexpected(MakeError(
×
488
                        SetupError,
489
                        "Failed to find the required size of the signature buffer: "
490
                                + GetOpenSSLErrorMessage()));
×
491
        }
492

493
        vector<uint8_t> sig(sig_len);
2✔
494
        ret = EVP_DigestSign(md_ctx.get(), sig.data(), &sig_len, raw_data.data(), raw_data.size());
2✔
495
        if (ret != OPENSSL_SUCCESS) {
2✔
496
                return expected::unexpected(
×
497
                        MakeError(SetupError, "Failed to sign the message: " + GetOpenSSLErrorMessage()));
×
498
        }
499

500
        // The signature may in some cases be shorter than the previously allocated
501
        // length (which is the max)
502
        sig.resize(sig_len);
2✔
503

504
        return sig;
2✔
505
}
506

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

511
        if (EVP_PKEY_sign_init(pkey_signer_ctx.get()) <= 0) {
8✔
512
                return expected::unexpected(MakeError(
×
513
                        SetupError, "Failed to initialize the OpenSSL signer: " + GetOpenSSLErrorMessage()));
×
514
        }
515
        if (EVP_PKEY_CTX_set_signature_md(pkey_signer_ctx.get(), EVP_sha256()) <= 0) {
8✔
516
                return expected::unexpected(MakeError(
×
517
                        SetupError,
518
                        "Failed to set the OpenSSL signature to sha256: " + GetOpenSSLErrorMessage()));
×
519
        }
520

521
        vector<uint8_t> signature {};
522

523
        // Set the needed signature buffer length
524
        size_t digestlength = MENDER_DIGEST_SHA256_LENGTH, siglength;
525
        if (EVP_PKEY_sign(pkey_signer_ctx.get(), nullptr, &siglength, digest.data(), digestlength)
8✔
526
                <= 0) {
527
                return expected::unexpected(MakeError(
×
528
                        SetupError, "Failed to get the signature buffer length: " + GetOpenSSLErrorMessage()));
×
529
        }
530
        signature.resize(siglength);
8✔
531

532
        if (EVP_PKEY_sign(
8✔
533
                        pkey_signer_ctx.get(), signature.data(), &siglength, digest.data(), digestlength)
534
                <= 0) {
535
                return expected::unexpected(
×
536
                        MakeError(SetupError, "Failed to sign the digest: " + GetOpenSSLErrorMessage()));
×
537
        }
538

539
        // The signature may in some cases be shorter than the previously allocated
540
        // length (which is the max)
541
        signature.resize(siglength);
8✔
542

543
        return signature;
8✔
544
}
545

546
expected::ExpectedBytes SignData(const Args &args, const vector<uint8_t> &raw_data) {
11✔
547
        auto exp_private_key = PrivateKey::Load(args);
11✔
548
        if (!exp_private_key) {
11✔
549
                return expected::unexpected(exp_private_key.error());
2✔
550
        }
551

552
        log::Info("Signing with: " + args.private_key_path);
10✔
553

554
        auto key_type = EVP_PKEY_base_id(exp_private_key.value().Get());
10✔
555

556
        // ED25519 signatures need to be handled independently, because of how the
557
        // signature scheme is designed.
558
        if (key_type == EVP_PKEY_ED25519) {
10✔
559
                return SignED25519(exp_private_key.value().Get(), raw_data);
2✔
560
        }
561

562
        auto exp_shasum = mender::sha::Shasum(raw_data);
8✔
563
        if (!exp_shasum) {
8✔
564
                return expected::unexpected(exp_shasum.error());
×
565
        }
566
        auto digest = exp_shasum.value(); /* The shasummed data = digest in crypto world */
8✔
567
        log::Debug("Shasum is: " + digest.String());
16✔
568

569
        return SignGeneric(exp_private_key.value(), digest);
16✔
570
}
571

572
expected::ExpectedString Sign(const Args &args, const vector<uint8_t> &raw_data) {
11✔
573
        auto exp_signed_data = SignData(args, raw_data);
11✔
574
        if (!exp_signed_data) {
11✔
575
                return expected::unexpected(exp_signed_data.error());
2✔
576
        }
577
        vector<uint8_t> signature = exp_signed_data.value();
10✔
578

579
        return EncodeBase64(signature);
20✔
580
}
581

582
const size_t mender_decode_buf_size = 256;
583
const size_t ecdsa256keySize = 32;
584

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

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

632
        // Set the r&s values in the SIG struct
633
        // r & s now owned by ecSig
634
        int ret {ECDSA_SIG_set0(ecSig.get(), r.get(), s.get())};
2✔
635
        if (ret != OPENSSL_SUCCESS) {
2✔
636
                return expected::unexpected(MakeError(
×
637
                        SetupError,
638
                        "Failed to set the signature parts in the ECDSA structure: "
639
                                + GetOpenSSLErrorMessage()));
×
640
        }
641
        r.release();
642
        s.release();
643

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

657
        return der_encoded_byte_array;
2✔
658
}
659

660

661
expected::ExpectedBool VerifySignData(
662
        const string &public_key_path,
663
        const mender::sha::SHA &shasum,
664
        const vector<uint8_t> &signature);
665

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

684
        vector<uint8_t> der_encoded_signature = exp_der_encoded_signature.value();
2✔
685

686
        return VerifySignData(public_key_path, shasum, der_encoded_signature);
2✔
687
}
688

689
static bool OpenSSLSignatureVerificationError(int a) {
690
        /*
691
         * The signature check errored. This is different from the signature being
692
         * wrong. We simply were not able to perform the check in this instance.
693
         * Therefore, we fall back to trying the custom marshalled binary ECDSA
694
         * signature, which we have been using in Mender.
695
         */
696
        return a < 0;
697
}
698

699
expected::ExpectedBool VerifySignData(
16✔
700
        const string &public_key_path,
701
        const mender::sha::SHA &shasum,
702
        const vector<uint8_t> &signature) {
703
        auto bio_key =
704
                unique_ptr<BIO, void (*)(BIO *)>(BIO_new_file(public_key_path.c_str(), "r"), bio_free_func);
32✔
705
        if (bio_key == nullptr) {
16✔
706
                return expected::unexpected(MakeError(
3✔
707
                        SetupError,
708
                        "Failed to open the public key file from (" + public_key_path
6✔
709
                                + "):" + GetOpenSSLErrorMessage()));
15✔
710
        }
711

712
        auto pkey = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
713
                PEM_read_bio_PUBKEY(bio_key.get(), nullptr, nullptr, nullptr), pkey_free_func);
26✔
714
        if (pkey == nullptr) {
13✔
715
                return expected::unexpected(MakeError(
2✔
716
                        SetupError,
717
                        "Failed to load the public key from(" + public_key_path
4✔
718
                                + "): " + GetOpenSSLErrorMessage()));
10✔
719
        }
720

721
        auto pkey_signer_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
722
                EVP_PKEY_CTX_new(pkey.get(), nullptr), pkey_ctx_free_func);
22✔
723

724
        auto ret = EVP_PKEY_verify_init(pkey_signer_ctx.get());
11✔
725
        if (ret <= 0) {
11✔
726
                return expected::unexpected(MakeError(
×
727
                        SetupError, "Failed to initialize the OpenSSL signer: " + GetOpenSSLErrorMessage()));
×
728
        }
729
        ret = EVP_PKEY_CTX_set_signature_md(pkey_signer_ctx.get(), EVP_sha256());
11✔
730
        if (ret <= 0) {
11✔
731
                return expected::unexpected(MakeError(
×
732
                        SetupError,
733
                        "Failed to set the OpenSSL signature to sha256: " + GetOpenSSLErrorMessage()));
×
734
        }
735

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

752
expected::ExpectedBool VerifySign(
14✔
753
        const string &public_key_path, const mender::sha::SHA &shasum, const string &signature) {
754
        // signature: decode base64
755
        auto exp_decoded_signature = DecodeBase64(signature);
28✔
756
        if (!exp_decoded_signature) {
14✔
757
                return expected::unexpected(exp_decoded_signature.error());
×
758
        }
759
        auto decoded_signature = exp_decoded_signature.value();
14✔
760

761
        return VerifySignData(public_key_path, shasum, decoded_signature);
14✔
762
}
763

764
error::Error PrivateKey::SaveToPEM(const string &private_key_path) {
6✔
765
        auto bio_key = unique_ptr<BIO, void (*)(BIO *)>(
766
                BIO_new_file(private_key_path.c_str(), "w"), bio_free_func);
12✔
767
        if (bio_key == nullptr) {
6✔
768
                return MakeError(
769
                        SetupError,
770
                        "Failed to open the private key file (" + private_key_path
2✔
771
                                + "): " + GetOpenSSLErrorMessage());
4✔
772
        }
773

774
        auto ret =
775
                PEM_write_bio_PrivateKey(bio_key.get(), key.get(), nullptr, nullptr, 0, nullptr, nullptr);
5✔
776
        if (ret != OPENSSL_SUCCESS) {
5✔
777
                return MakeError(
778
                        SetupError,
779
                        "Failed to save the private key to file (" + private_key_path
×
780
                                + "): " + GetOpenSSLErrorMessage());
×
781
        }
782

783
        return error::NoError;
5✔
784
}
785

786
} // namespace crypto
787
} // namespace common
788
} // 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