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

mendersoftware / mender / 1046247406

23 Oct 2023 09:07AM UTC coverage: 80.105% (-0.1%) from 80.226%
1046247406

push

gitlab-ci

oleorhagen
feat(crypto): Add HSM support

This adds support for HSM key storage. This is a wrapper around the existing
OpenSSL external crypto support, and supports both the deprecated `ENGINE` API
in OpenSSL 1.x, and the new `Provider` API in OpenSSL 3.x.

Ticket: MEN-6668
Changelog: The client's HSM crypto-module support is changed so that the
`PrivateKey` used for `authentication` is always taken from the configurations:
`security.AuthPrivateKey`, and the `HttpsClient.private_key` is only used as the
key for the associated certificate `HttpsClient.client_certificate`. The two can
still use the same key, but this means now that you add the same key `url` in
both places.

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

66 of 66 new or added lines in 7 files covered. (100.0%)

6845 of 8545 relevant lines covered (80.11%)

9412.89 hits per line

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

67.26
/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 <cstdint>
18
#include <string>
19
#include <vector>
20
#include <memory>
21

22

23
#ifdef MENDER_CRYPTO_OPENSSL_LEGACY
24
#include <openssl/engine.h>
25
#else
26
#include <openssl/provider.h>
27
#include <openssl/store.h>
28
#endif // MENDER_CRYPTO_OPENSSL_LEGACY
29

30
#include <openssl/evp.h>
31
#include <openssl/pem.h>
32
#include <openssl/err.h>
33
#include <openssl/rsa.h>
34
#include <openssl/bn.h>
35

36
#include <common/io.hpp>
37
#include <common/error.hpp>
38
#include <common/expected.hpp>
39
#include <common/common.hpp>
40

41
#include <artifact/sha/sha.hpp>
42

43

44
namespace mender {
45
namespace common {
46
namespace crypto {
47

48
const size_t MENDER_DIGEST_SHA256_LENGTH = 32;
49

50
const size_t OPENSSL_SUCCESS = 1;
51

52
using namespace std;
53

54
namespace error = mender::common::error;
55
namespace io = mender::common::io;
56

57
auto pkey_ctx_free_func = [](EVP_PKEY_CTX *ctx) {
23✔
58
        if (ctx) {
23✔
59
                EVP_PKEY_CTX_free(ctx);
23✔
60
        }
61
};
23✔
62
auto pkey_free_func = [](EVP_PKEY *key) {
38✔
63
        if (key) {
38✔
64
                EVP_PKEY_free(key);
38✔
65
        }
66
};
38✔
67
auto bio_free_func = [](BIO *bio) {
41✔
68
        if (bio) {
41✔
69
                BIO_free(bio);
41✔
70
        }
71
};
41✔
72
auto bio_free_all_func = [](BIO *bio) {
6✔
73
        if (bio) {
6✔
74
                BIO_free_all(bio);
6✔
75
        }
76
};
6✔
77
#ifdef MENDER_CRYPTO_OPENSSL_LEGACY
78
auto bn_free = [](BIGNUM *bn) {
×
79
        if (bn) {
×
80
                BN_free(bn);
×
81
        }
82
};
×
83
auto engine_free_func = [](ENGINE *e) {
×
84
        if (e) {
×
85
                ENGINE_free(e);
×
86
        }
87
};
×
88
#endif
89

90
// NOTE: GetOpenSSLErrorMessage should be called upon all OpenSSL errors, as
91
// the errors are queued, and if not harvested, the FIFO structure of the
92
// queue will mean that if you just get one, you might actually get the wrong
93
// one.
94
string GetOpenSSLErrorMessage() {
16✔
95
        const auto sysErrorCode = errno;
16✔
96
        auto sslErrorCode = ERR_get_error();
16✔
97

98
        std::string errorDescription {};
99
        while (sslErrorCode != 0) {
51✔
100
                if (!errorDescription.empty()) {
35✔
101
                        errorDescription += '\n';
102
                }
103
                errorDescription += ERR_error_string(sslErrorCode, nullptr);
35✔
104
                sslErrorCode = ERR_get_error();
35✔
105
        }
106
        if (sysErrorCode != 0) {
16✔
107
                if (!errorDescription.empty()) {
16✔
108
                        errorDescription += '\n';
109
                }
110
                errorDescription += "System error, code=" + std::to_string(sysErrorCode);
32✔
111
                errorDescription += ", ";
16✔
112
                errorDescription += strerror(sysErrorCode);
16✔
113
        }
114
        return errorDescription;
16✔
115
}
116

117
ExpectedPrivateKey PrivateKey::LoadFromPEM(
33✔
118
        const string &private_key_path, const string &passphrase) {
119
        log::Trace("Loading private key from file: " + private_key_path);
66✔
120
        auto private_bio_key = unique_ptr<BIO, void (*)(BIO *)>(
121
                BIO_new_file(private_key_path.c_str(), "r"), bio_free_func);
66✔
122
        if (private_bio_key == nullptr) {
33✔
123
                return expected::unexpected(MakeError(
6✔
124
                        SetupError,
125
                        "Failed to open the private key file " + private_key_path + ": "
12✔
126
                                + GetOpenSSLErrorMessage()));
30✔
127
        }
128

129
        vector<char> chars(passphrase.begin(), passphrase.end());
27✔
130
        chars.push_back('\0');
27✔
131
        char *c_str = chars.data();
132

133
        // We need our own custom callback routine, as the default one will prompt
134
        // for a passphrase.
135
        auto callback = [](char *buf, int size, int rwflag, void *u) {
3✔
136
                // We'll only use this callback for reading passphrases, not for
137
                // writing them.
138
                assert(rwflag == 0);
139

140
                if (u == nullptr) {
3✔
141
                        return 0;
142
                }
143

144
                // NB: buf is not expected to be null terminated.
145
                char *const pass = static_cast<char *>(u);
146
                strncpy(buf, pass, size);
3✔
147

148
                const int len = static_cast<int>(strlen(pass));
3✔
149
                return (len < size) ? len : size;
150
        };
151

152
        auto private_key = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
153
                PEM_read_bio_PrivateKey(private_bio_key.get(), nullptr, callback, c_str), pkey_free_func);
54✔
154
        if (private_key == nullptr) {
27✔
155
                return expected::unexpected(MakeError(
4✔
156
                        SetupError,
157
                        "Failed to load the key: " + private_key_path + " " + GetOpenSSLErrorMessage()));
12✔
158
        }
159

160
        return PrivateKey(std::move(private_key));
23✔
161
}
162

163
#ifdef MENDER_CRYPTO_OPENSSL_LEGACY
164
ExpectedPrivateKey PrivateKey::LoadFromHSM(const Args &args) {
×
165
        log::Trace("Loading the private key from HSM");
×
166

167
        ENGINE_load_builtin_engines();
×
168
        auto engine = unique_ptr<ENGINE, void (*)(ENGINE *)>(
169
                ENGINE_by_id(args.ssl_engine.c_str()), engine_free_func);
×
170

171
        if (engine == nullptr) {
×
172
                return expected::unexpected(MakeError(
×
173
                        SetupError,
174
                        "Failed to get the " + args.ssl_engine
×
175
                                + " engine. No engine with the ID found: " + GetOpenSSLErrorMessage()));
×
176
        }
177
        log::Debug("Loaded the HSM engine successfully!");
×
178

179
        int res = ENGINE_init(engine.get());
×
180
        if (not res) {
×
181
                return expected::unexpected(MakeError(
×
182
                        SetupError,
183
                        "Failed to initialise the hardware security module (HSM): "
184
                                + GetOpenSSLErrorMessage()));
×
185
        }
186
        log::Debug("Successfully initialised the HSM engine");
×
187

188
        // Load the private key
189
        void *ui_method = NULL;
190
        void *callback_data = NULL;
191
        auto private_key = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
192
                ENGINE_load_private_key(
193
                        engine.get(), args.private_key_path.c_str(), (UI_METHOD *) ui_method, callback_data),
194
                pkey_free_func);
×
195
        if (private_key == nullptr) {
×
196
                return expected::unexpected(MakeError(
×
197
                        SetupError,
198
                        "Failed to load the private key from the hardware security module: "
199
                                + GetOpenSSLErrorMessage()));
×
200
        }
201
        log::Debug("Successfully loaded the private key from the HSM Engine: " + args.ssl_engine);
×
202

203
        return PrivateKey(std::move(private_key), std::move(engine));
×
204
}
205
#endif // MENDER_CRYPTO_OPENSSL_LEGACY
206

207
#ifndef MENDER_CRYPTO_OPENSSL_LEGACY
208
ExpectedPrivateKey PrivateKey::LoadFromHSM(const Args &args) {
209
        log::Debug("Loading the private key from HSM");
210

211
        auto default_provider = unique_ptr<OSSL_PROVIDER, int (*)(OSSL_PROVIDER *)>(
212
                OSSL_PROVIDER_load(NULL, "default"), OSSL_PROVIDER_unload);
213
        if (default_provider == nullptr) {
214
                return expected::unexpected(
215
                        MakeError(SetupError, "default provider load error: " + GetOpenSSLErrorMessage()));
216
        }
217

218
        auto hsm_provider = unique_ptr<OSSL_PROVIDER, int (*)(OSSL_PROVIDER *)>(
219
                OSSL_PROVIDER_load(NULL, args.ssl_engine.c_str()), OSSL_PROVIDER_unload);
220
        if (hsm_provider == nullptr) {
221
                return expected::unexpected(MakeError(
222
                        SetupError, args.ssl_engine + " provider load error: " + GetOpenSSLErrorMessage()));
223
        }
224

225
        int ret {OSSL_PROVIDER_available(NULL, args.ssl_engine.c_str())};
226
        if (ret != OPENSSL_SUCCESS) {
227
                return expected::unexpected(MakeError(
228
                        SetupError, args.ssl_engine + " provider not available: " + GetOpenSSLErrorMessage()));
229
        }
230

231
        log::Trace("Loading key: " + args.private_key_path);
232
        auto ctx = unique_ptr<OSSL_STORE_CTX, int (*)(OSSL_STORE_CTX *)>(
233
                OSSL_STORE_open(args.private_key_path.c_str(), NULL, NULL, NULL, NULL), OSSL_STORE_close);
234

235
        if (ctx == nullptr) {
236
                return expected::unexpected(MakeError(
237
                        SetupError,
238
                        "OSSL_STORE_OPEN: Failed to load the private key from the hardware security module: "
239
                                + GetOpenSSLErrorMessage()));
240
        }
241

242
        // Go through all objects in the context till we find the first private key
243
        while (not OSSL_STORE_eof(ctx.get())) {
244
                OSSL_STORE_INFO *info = OSSL_STORE_load(ctx.get());
245

246
                if (info == NULL) {
247
                        return expected::unexpected(MakeError(
248
                                SetupError,
249
                                "Failed to read the private key information the hardware security module: "
250
                                        + GetOpenSSLErrorMessage()));
251
                }
252

253
                const int type_info {OSSL_STORE_INFO_get_type(info)};
254
                switch (type_info) {
255
                case OSSL_STORE_INFO_PKEY: {
256
                        // NOTE: get1 creates a duplicate of the pkey from the info, which can be
257
                        // used after the info ctx is destroyed
258
                        auto private_key = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
259
                                OSSL_STORE_INFO_get1_PKEY(info), pkey_free_func);
260
                        if (private_key == nullptr) {
261
                                return expected::unexpected(MakeError(
262
                                        SetupError,
263
                                        "Failed to load the private key from the hardware security module: "
264
                                                + GetOpenSSLErrorMessage()));
265
                        }
266
                        log::Debug("Successfully loaded the private key");
267
                        return PrivateKey(
268
                                std::move(private_key), std::move(default_provider), std::move(hsm_provider));
269
                }
270
                default:
271
                        const string info_type_string = OSSL_STORE_INFO_type_string(type_info);
272
                        return expected::unexpected(MakeError(
273
                                SetupError,
274
                                "Unhandled OpenSSL type: expected PrivateKey, got: " + info_type_string));
275
                        break;
276
                }
277
        }
278

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

286
ExpectedPrivateKey PrivateKey::Load(const Args &args) {
25✔
287
        log::Trace("Loading private key");
50✔
288
        if (args.ssl_engine != "") {
25✔
289
                return LoadFromHSM(args);
×
290
        }
291
        return LoadFromPEM(args.private_key_path, args.private_key_passphrase);
25✔
292
}
293

294
ExpectedPrivateKey PrivateKey::Generate(const unsigned int bits, const unsigned int exponent) {
8✔
295
#ifdef MENDER_CRYPTO_OPENSSL_LEGACY
296
        auto pkey_gen_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
297
                EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr), pkey_ctx_free_func);
16✔
298

299
        int ret = EVP_PKEY_keygen_init(pkey_gen_ctx.get());
8✔
300
        if (ret != OPENSSL_SUCCESS) {
8✔
301
                return expected::unexpected(MakeError(
×
302
                        SetupError,
303
                        "Failed to generate a private key. Initialization failed: "
304
                                + GetOpenSSLErrorMessage()));
×
305
        }
306

307
        ret = EVP_PKEY_CTX_set_rsa_keygen_bits(pkey_gen_ctx.get(), bits);
8✔
308
        if (ret != OPENSSL_SUCCESS) {
8✔
309
                return expected::unexpected(MakeError(
×
310
                        SetupError,
311
                        "Failed to generate a private key. Parameters setting failed: "
312
                                + GetOpenSSLErrorMessage()));
×
313
        }
314

315
        auto exponent_bn = unique_ptr<BIGNUM, void (*)(BIGNUM *)>(BN_new(), bn_free);
16✔
316
        ret = BN_set_word(exponent_bn.get(), exponent);
8✔
317
        if (ret != OPENSSL_SUCCESS) {
8✔
318
                return expected::unexpected(MakeError(
×
319
                        SetupError,
320
                        "Failed to generate a private key. Parameters setting failed: "
321
                                + GetOpenSSLErrorMessage()));
×
322
        }
323

324
        ret = EVP_PKEY_CTX_set_rsa_keygen_pubexp(pkey_gen_ctx.get(), exponent_bn.get());
8✔
325
        if (ret != OPENSSL_SUCCESS) {
8✔
326
                return expected::unexpected(MakeError(
×
327
                        SetupError,
328
                        "Failed to generate a private key. Parameters setting failed: "
329
                                + GetOpenSSLErrorMessage()));
×
330
        }
331
        exponent_bn.release();
332

333
        EVP_PKEY *pkey = nullptr;
8✔
334
        ret = EVP_PKEY_keygen(pkey_gen_ctx.get(), &pkey);
8✔
335
        if (ret != OPENSSL_SUCCESS) {
8✔
336
                return expected::unexpected(MakeError(
×
337
                        SetupError,
338
                        "Failed to generate a private key. Generation failed: " + GetOpenSSLErrorMessage()));
×
339
        }
340
#else
341
        auto pkey_gen_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
342
                EVP_PKEY_CTX_new_from_name(nullptr, "RSA", nullptr), pkey_ctx_free_func);
343

344
        int ret = EVP_PKEY_keygen_init(pkey_gen_ctx.get());
345
        if (ret != OPENSSL_SUCCESS) {
346
                return expected::unexpected(MakeError(
347
                        SetupError,
348
                        "Failed to generate a private key. Initialization failed: "
349
                                + GetOpenSSLErrorMessage()));
350
        }
351

352
        OSSL_PARAM params[3];
353
        auto bits_buffer = bits;
354
        auto exponent_buffer = exponent;
355
        params[0] = OSSL_PARAM_construct_uint("bits", &bits_buffer);
356
        params[1] = OSSL_PARAM_construct_uint("e", &exponent_buffer);
357
        params[2] = OSSL_PARAM_construct_end();
358

359
        ret = EVP_PKEY_CTX_set_params(pkey_gen_ctx.get(), params);
360
        if (ret != OPENSSL_SUCCESS) {
361
                return expected::unexpected(MakeError(
362
                        SetupError,
363
                        "Failed to generate a private key. Parameters setting failed: "
364
                                + GetOpenSSLErrorMessage()));
365
        }
366

367
        EVP_PKEY *pkey = nullptr;
368
        ret = EVP_PKEY_generate(pkey_gen_ctx.get(), &pkey);
369
        if (ret != OPENSSL_SUCCESS) {
370
                return expected::unexpected(MakeError(
371
                        SetupError,
372
                        "Failed to generate a private key. Generation failed: " + GetOpenSSLErrorMessage()));
373
        }
374
#endif
375

376
        auto private_key = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(pkey, pkey_free_func);
8✔
377
        return PrivateKey(std::move(private_key));
8✔
378
}
379

380
expected::ExpectedString EncodeBase64(vector<uint8_t> to_encode) {
9✔
381
        // Predict the len of the decoded for later verification. From man page:
382
        // For every 3 bytes of input provided 4 bytes of output
383
        // data will be produced. If n is not divisible by 3 (...)
384
        // the output is padded such that it is always divisible by 4.
385
        const uint64_t predicted_len {4 * ((to_encode.size() + 2) / 3)};
9✔
386

387
        // Add space for a NUL terminator. From man page:
388
        // Additionally a NUL terminator character will be added
389
        auto buffer {vector<unsigned char>(predicted_len + 1)};
9✔
390

391
        const int64_t output_len {
392
                EVP_EncodeBlock(buffer.data(), to_encode.data(), static_cast<int>(to_encode.size()))};
9✔
393
        assert(output_len >= 0);
394

395
        if (predicted_len != static_cast<uint64_t>(output_len)) {
9✔
396
                return expected::unexpected(
×
397
                        MakeError(Base64Error, "The predicted and the actual length differ"));
×
398
        }
399

400
        return string(buffer.begin(), buffer.end() - 1); // Remove the last zero byte
18✔
401
}
402

403
expected::ExpectedBytes DecodeBase64(string to_decode) {
13✔
404
        // Predict the len of the decoded for later verification. From man page:
405
        // For every 4 input bytes exactly 3 output bytes will be
406
        // produced. The output will be padded with 0 bits if necessary
407
        // to ensure that the output is always 3 bytes.
408
        const uint64_t predicted_len {3 * ((to_decode.size() + 3) / 4)};
13✔
409

410
        auto buffer {vector<unsigned char>(predicted_len)};
13✔
411

412
        const int64_t output_len {EVP_DecodeBlock(
13✔
413
                buffer.data(),
414
                common::ByteVectorFromString(to_decode).data(),
13✔
415
                static_cast<int>(to_decode.size()))};
13✔
416
        assert(output_len >= 0);
417

418
        if (predicted_len != static_cast<uint64_t>(output_len)) {
13✔
419
                return expected::unexpected(MakeError(
×
420
                        Base64Error,
421
                        "The predicted (" + std::to_string(predicted_len) + ") and the actual ("
×
422
                                + std::to_string(output_len) + ") length differ"));
×
423
        }
424

425
        // Subtract padding bytes. Inspired by internal OpenSSL code from:
426
        // https://github.com/openssl/openssl/blob/ff88545e02ab48a52952350c52013cf765455dd3/crypto/ct/ct_b64.c#L46
427
        for (auto it = to_decode.crbegin(); *it == '='; it++) {
17✔
428
                buffer.pop_back();
429
        }
430

431
        return buffer;
13✔
432
}
433

434

435
expected::ExpectedString ExtractPublicKey(const Args &args) {
7✔
436
        auto exp_private_key = PrivateKey::Load(args);
7✔
437
        if (!exp_private_key) {
7✔
438
                return expected::unexpected(exp_private_key.error());
2✔
439
        }
440

441
        auto bio_public_key = unique_ptr<BIO, void (*)(BIO *)>(BIO_new(BIO_s_mem()), bio_free_all_func);
12✔
442

443
        if (!bio_public_key.get()) {
6✔
444
                return expected::unexpected(MakeError(
×
445
                        SetupError,
446
                        "Failed to extract the public key from the private key " + args.private_key_path
×
447
                                + "):" + GetOpenSSLErrorMessage()));
×
448
        }
449

450
        int ret = PEM_write_bio_PUBKEY(bio_public_key.get(), exp_private_key.value().Get());
6✔
451
        if (ret != OPENSSL_SUCCESS) {
6✔
452
                return expected::unexpected(MakeError(
×
453
                        SetupError,
454
                        "Failed to extract the public key from: (" + args.private_key_path
×
455
                                + "): OpenSSL BIO write failed: " + GetOpenSSLErrorMessage()));
×
456
        }
457

458
        int pending = BIO_ctrl_pending(bio_public_key.get());
6✔
459
        if (pending <= 0) {
6✔
460
                return expected::unexpected(MakeError(
×
461
                        SetupError,
462
                        "Failed to extract the public key from: (" + args.private_key_path
×
463
                                + "): Zero byte key unexpected: " + GetOpenSSLErrorMessage()));
×
464
        }
465

466
        vector<uint8_t> key_vector(pending);
6✔
467

468
        size_t read = BIO_read(bio_public_key.get(), key_vector.data(), pending);
6✔
469

470
        if (read == 0) {
6✔
471
                MakeError(
×
472
                        SetupError,
473
                        "Failed to extract the public key from (" + args.private_key_path
×
474
                                + "): Zero bytes read from BIO: " + GetOpenSSLErrorMessage());
×
475
        }
476

477
        return string(key_vector.begin(), key_vector.end());
12✔
478
}
479

480
expected::ExpectedBytes SignData(const Args &args, const vector<uint8_t> &digest) {
9✔
481
        auto exp_private_key = PrivateKey::Load(args);
9✔
482
        if (!exp_private_key) {
9✔
483
                return expected::unexpected(exp_private_key.error());
2✔
484
        }
485

486
        auto pkey_signer_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
487
                EVP_PKEY_CTX_new(exp_private_key.value().Get(), nullptr), pkey_ctx_free_func);
16✔
488

489
        if (EVP_PKEY_sign_init(pkey_signer_ctx.get()) <= 0) {
8✔
490
                return expected::unexpected(MakeError(
×
491
                        SetupError, "Failed to initialize the OpenSSL signer: " + GetOpenSSLErrorMessage()));
×
492
        }
493
        if (EVP_PKEY_CTX_set_signature_md(pkey_signer_ctx.get(), EVP_sha256()) <= 0) {
8✔
494
                return expected::unexpected(MakeError(
×
495
                        SetupError,
496
                        "Failed to set the OpenSSL signature to sha256: " + GetOpenSSLErrorMessage()));
×
497
        }
498

499
        vector<uint8_t> signature {};
500

501
        // Set the needed signature buffer length
502
        size_t digestlength = MENDER_DIGEST_SHA256_LENGTH, siglength;
503
        if (EVP_PKEY_sign(pkey_signer_ctx.get(), nullptr, &siglength, digest.data(), digestlength)
8✔
504
                <= 0) {
505
                return expected::unexpected(MakeError(
×
506
                        SetupError, "Failed to get the signature buffer length: " + GetOpenSSLErrorMessage()));
×
507
        }
508
        signature.resize(siglength);
8✔
509

510
        if (EVP_PKEY_sign(
8✔
511
                        pkey_signer_ctx.get(), signature.data(), &siglength, digest.data(), digestlength)
512
                <= 0) {
513
                return expected::unexpected(
×
514
                        MakeError(SetupError, "Failed to sign the digest: " + GetOpenSSLErrorMessage()));
×
515
        }
516

517
        // The signature may in some cases be shorter than the previously allocated
518
        // length (which is the max)
519
        signature.resize(siglength);
8✔
520

521
        return signature;
8✔
522
}
523

524
expected::ExpectedString Sign(const Args &args, const mender::sha::SHA &shasum) {
9✔
525
        auto exp_signed_data = SignData(args, shasum);
18✔
526
        if (!exp_signed_data) {
9✔
527
                return expected::unexpected(exp_signed_data.error());
2✔
528
        }
529
        vector<uint8_t> signature = exp_signed_data.value();
8✔
530

531
        return EncodeBase64(signature);
16✔
532
}
533

534
expected::ExpectedString SignRawData(const Args &args, const vector<uint8_t> &raw_data) {
8✔
535
        auto exp_shasum = mender::sha::Shasum(raw_data);
8✔
536

537
        if (!exp_shasum) {
8✔
538
                return expected::unexpected(exp_shasum.error());
×
539
        }
540
        auto shasum = exp_shasum.value();
8✔
541
        log::Debug("Shasum is: " + shasum.String());
16✔
542

543
        return Sign(args, shasum);
8✔
544
}
545

546
expected::ExpectedBool VerifySignData(
12✔
547
        const string &public_key_path,
548
        const mender::sha::SHA &shasum,
549
        const vector<uint8_t> &signature) {
550
        auto bio_key =
551
                unique_ptr<BIO, void (*)(BIO *)>(BIO_new_file(public_key_path.c_str(), "r"), bio_free_func);
24✔
552
        if (bio_key == nullptr) {
12✔
553
                return expected::unexpected(MakeError(
3✔
554
                        SetupError,
555
                        "Failed to open the public key file from (" + public_key_path
6✔
556
                                + "):" + GetOpenSSLErrorMessage()));
15✔
557
        }
558

559
        auto pkey = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
560
                PEM_read_bio_PUBKEY(bio_key.get(), nullptr, nullptr, nullptr), pkey_free_func);
18✔
561
        if (pkey == nullptr) {
9✔
562
                return expected::unexpected(MakeError(
2✔
563
                        SetupError,
564
                        "Failed to load the public key from(" + public_key_path
4✔
565
                                + "): " + GetOpenSSLErrorMessage()));
10✔
566
        }
567

568
        // prepare context
569
        auto pkey_signer_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
570
                EVP_PKEY_CTX_new(pkey.get(), nullptr), pkey_ctx_free_func);
14✔
571

572
        auto ret = EVP_PKEY_verify_init(pkey_signer_ctx.get());
7✔
573
        if (ret <= 0) {
7✔
574
                return expected::unexpected(MakeError(
×
575
                        SetupError, "Failed to initialize the OpenSSL signer: " + GetOpenSSLErrorMessage()));
×
576
        }
577
        ret = EVP_PKEY_CTX_set_signature_md(pkey_signer_ctx.get(), EVP_sha256());
7✔
578
        if (ret <= 0) {
7✔
579
                return expected::unexpected(MakeError(
×
580
                        SetupError,
581
                        "Failed to set the OpenSSL signature to sha256: " + GetOpenSSLErrorMessage()));
×
582
        }
583

584
        // verify signature
585
        ret = EVP_PKEY_verify(
7✔
586
                pkey_signer_ctx.get(), signature.data(), signature.size(), shasum.data(), shasum.size());
587
        if (ret < 0) {
7✔
588
                return expected::unexpected(MakeError(
×
589
                        VerificationError,
590
                        "Failed to verify signature. OpenSSL PKEY verify failed: " + GetOpenSSLErrorMessage()));
×
591
        }
592

593
        return ret == OPENSSL_SUCCESS;
7✔
594
}
595

596
expected::ExpectedBool VerifySign(
12✔
597
        const string &public_key_path, const mender::sha::SHA &shasum, const string &signature) {
598
        // signature: decode base64
599
        auto exp_decoded_signature = DecodeBase64(signature);
24✔
600
        if (!exp_decoded_signature) {
12✔
601
                return expected::unexpected(exp_decoded_signature.error());
×
602
        }
603
        auto decoded_signature = exp_decoded_signature.value();
12✔
604

605
        return VerifySignData(public_key_path, shasum, decoded_signature);
12✔
606
}
607

608
error::Error PrivateKey::SaveToPEM(const string &private_key_path) {
6✔
609
        auto bio_key = unique_ptr<BIO, void (*)(BIO *)>(
610
                BIO_new_file(private_key_path.c_str(), "w"), bio_free_func);
12✔
611
        if (bio_key == nullptr) {
6✔
612
                return MakeError(
613
                        SetupError,
614
                        "Failed to open the private key file (" + private_key_path
2✔
615
                                + "): " + GetOpenSSLErrorMessage());
4✔
616
        }
617

618
        // PEM_write_bio_PrivateKey_traditional will use the key-specific PKCS1
619
        // format if one is available for that key type, otherwise it will encode
620
        // to a PKCS8 key.
621
        auto ret = PEM_write_bio_PrivateKey_traditional(
5✔
622
                bio_key.get(), key.get(), nullptr, nullptr, 0, nullptr, nullptr);
623
        if (ret != OPENSSL_SUCCESS) {
5✔
624
                return MakeError(
625
                        SetupError,
626
                        "Failed to save the private key to file (" + private_key_path
×
627
                                + "): " + GetOpenSSLErrorMessage());
×
628
        }
629

630
        return error::NoError;
5✔
631
}
632

633
} // namespace crypto
634
} // namespace common
635
} // 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