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

mendersoftware / mender / 1033353997

11 Oct 2023 01:43PM UTC coverage: 79.99% (-0.2%) from 80.166%
1033353997

push

gitlab-ci

oleorhagen
style: Run clang-format on the whole repository

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

1 of 1 new or added line in 1 file covered. (100.0%)

6492 of 8116 relevant lines covered (79.99%)

9901.24 hits per line

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

74.76
/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/evp.h>
25
#include <openssl/pem.h>
26
#include <openssl/err.h>
27
#include <openssl/rsa.h>
28
#include <openssl/bn.h>
29

30
#include <common/io.hpp>
31
#include <common/error.hpp>
32
#include <common/expected.hpp>
33
#include <common/common.hpp>
34

35
#include <artifact/sha/sha.hpp>
36

37

38
namespace mender {
39
namespace common {
40
namespace crypto {
41

42
const size_t MENDER_DIGEST_SHA256_LENGTH = 32;
43

44
const size_t OPENSSL_SUCCESS = 1;
45

46
using namespace std;
47

48
namespace error = mender::common::error;
49
namespace io = mender::common::io;
50

51
auto pkey_ctx_free_func = [](EVP_PKEY_CTX *ctx) {
32✔
52
        if (ctx) {
32✔
53
                EVP_PKEY_CTX_free(ctx);
32✔
54
        }
55
};
32✔
56
auto pkey_free_func = [](EVP_PKEY *key) {
54✔
57
        if (key) {
54✔
58
                EVP_PKEY_free(key);
54✔
59
        }
60
};
54✔
61
auto bio_free_func = [](BIO *bio) {
57✔
62
        if (bio) {
57✔
63
                BIO_free(bio);
57✔
64
        }
65
};
57✔
66
auto bio_free_all_func = [](BIO *bio) {
15✔
67
        if (bio) {
15✔
68
                BIO_free_all(bio);
15✔
69
        }
70
};
15✔
71
#ifdef MENDER_CRYPTO_OPENSSL_LEGACY
72
auto bn_free = [](BIGNUM *bn) {
×
73
        if (bn) {
×
74
                BN_free(bn);
×
75
        }
76
};
×
77
#endif
78

79
// NOTE: GetOpenSSLErrorMessage should be called upon all OpenSSL errors, as
80
// the errors are queued, and if not harvested, the FIFO structure of the
81
// queue will mean that if you just get one, you might actually get the wrong
82
// one.
83
string GetOpenSSLErrorMessage() {
16✔
84
        const auto sysErrorCode = errno;
16✔
85
        auto sslErrorCode = ERR_get_error();
16✔
86

87
        std::string errorDescription {};
88
        while (sslErrorCode != 0) {
51✔
89
                if (!errorDescription.empty()) {
35✔
90
                        errorDescription += '\n';
91
                }
92
                errorDescription += ERR_error_string(sslErrorCode, nullptr);
35✔
93
                sslErrorCode = ERR_get_error();
35✔
94
        }
95
        if (sysErrorCode != 0) {
16✔
96
                if (!errorDescription.empty()) {
16✔
97
                        errorDescription += '\n';
98
                }
99
                errorDescription += "System error, code=" + std::to_string(sysErrorCode);
32✔
100
                errorDescription += ", ";
16✔
101
                errorDescription += strerror(sysErrorCode);
16✔
102
        }
103
        return errorDescription;
16✔
104
}
105

106
ExpectedPrivateKey PrivateKey::LoadFromPEM(
14✔
107
        const string &private_key_path, const string &passphrase) {
108
        auto private_bio_key = unique_ptr<BIO, void (*)(BIO *)>(
109
                BIO_new_file(private_key_path.c_str(), "r"), bio_free_func);
28✔
110
        if (private_bio_key == nullptr) {
14✔
111
                return expected::unexpected(MakeError(
3✔
112
                        SetupError,
113
                        "Failed to open the private key file " + private_key_path + ": "
6✔
114
                                + GetOpenSSLErrorMessage()));
15✔
115
        }
116

117
        vector<char> chars(passphrase.begin(), passphrase.end());
11✔
118
        chars.push_back('\0');
11✔
119
        char *c_str = chars.data();
120

121
        // We need our own custom callback routine, as the default one will prompt
122
        // for a passphrase.
123
        auto callback = [](char *buf, int size, int rwflag, void *u) {
3✔
124
                // We'll only use this callback for reading passphrases, not for
125
                // writing them.
126
                assert(rwflag == 0);
127

128
                if (u == nullptr) {
3✔
129
                        return 0;
130
                }
131

132
                // NB: buf is not expected to be null terminated.
133
                char *const pass = static_cast<char *>(u);
134
                strncpy(buf, pass, size);
3✔
135

136
                const int len = static_cast<int>(strlen(pass));
3✔
137
                return (len < size) ? len : size;
138
        };
139

140
        auto private_key = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
141
                PEM_read_bio_PrivateKey(private_bio_key.get(), nullptr, callback, c_str), pkey_free_func);
22✔
142
        if (private_key == nullptr) {
11✔
143
                return expected::unexpected(MakeError(
4✔
144
                        SetupError,
145
                        "Failed to load the key: " + private_key_path + " " + GetOpenSSLErrorMessage()));
12✔
146
        }
147

148
        return unique_ptr<PrivateKey>(new PrivateKey(std::move(private_key)));
7✔
149
}
150

151
ExpectedPrivateKey PrivateKey::LoadFromPEM(const string &private_key_path) {
6✔
152
        return PrivateKey::LoadFromPEM(private_key_path, "");
12✔
153
}
154

155
ExpectedPrivateKey PrivateKey::Generate(const unsigned int bits, const unsigned int exponent) {
8✔
156
#ifdef MENDER_CRYPTO_OPENSSL_LEGACY
157
        auto pkey_gen_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
158
                EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr), pkey_ctx_free_func);
16✔
159

160
        int ret = EVP_PKEY_keygen_init(pkey_gen_ctx.get());
8✔
161
        if (ret != OPENSSL_SUCCESS) {
8✔
162
                return expected::unexpected(MakeError(
×
163
                        SetupError,
164
                        "Failed to generate a private key. Initialization failed: "
165
                                + GetOpenSSLErrorMessage()));
×
166
        }
167

168
        ret = EVP_PKEY_CTX_set_rsa_keygen_bits(pkey_gen_ctx.get(), bits);
8✔
169
        if (ret != OPENSSL_SUCCESS) {
8✔
170
                return expected::unexpected(MakeError(
×
171
                        SetupError,
172
                        "Failed to generate a private key. Parameters setting failed: "
173
                                + GetOpenSSLErrorMessage()));
×
174
        }
175

176
        auto exponent_bn = unique_ptr<BIGNUM, void (*)(BIGNUM *)>(BN_new(), bn_free);
16✔
177
        ret = BN_set_word(exponent_bn.get(), exponent);
8✔
178
        if (ret != OPENSSL_SUCCESS) {
8✔
179
                return expected::unexpected(MakeError(
×
180
                        SetupError,
181
                        "Failed to generate a private key. Parameters setting failed: "
182
                                + GetOpenSSLErrorMessage()));
×
183
        }
184

185
        ret = EVP_PKEY_CTX_set_rsa_keygen_pubexp(pkey_gen_ctx.get(), exponent_bn.get());
8✔
186
        if (ret != OPENSSL_SUCCESS) {
8✔
187
                return expected::unexpected(MakeError(
×
188
                        SetupError,
189
                        "Failed to generate a private key. Parameters setting failed: "
190
                                + GetOpenSSLErrorMessage()));
×
191
        }
192
        exponent_bn.release();
193

194
        EVP_PKEY *pkey = nullptr;
8✔
195
        ret = EVP_PKEY_keygen(pkey_gen_ctx.get(), &pkey);
8✔
196
        if (ret != OPENSSL_SUCCESS) {
8✔
197
                return expected::unexpected(MakeError(
×
198
                        SetupError,
199
                        "Failed to generate a private key. Generation failed: " + GetOpenSSLErrorMessage()));
×
200
        }
201
#else
202
        auto pkey_gen_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
203
                EVP_PKEY_CTX_new_from_name(nullptr, "RSA", nullptr), pkey_ctx_free_func);
204

205
        int ret = EVP_PKEY_keygen_init(pkey_gen_ctx.get());
206
        if (ret != OPENSSL_SUCCESS) {
207
                return expected::unexpected(MakeError(
208
                        SetupError,
209
                        "Failed to generate a private key. Initialization failed: "
210
                                + GetOpenSSLErrorMessage()));
211
        }
212

213
        OSSL_PARAM params[3];
214
        auto bits_buffer = bits;
215
        auto exponent_buffer = exponent;
216
        params[0] = OSSL_PARAM_construct_uint("bits", &bits_buffer);
217
        params[1] = OSSL_PARAM_construct_uint("e", &exponent_buffer);
218
        params[2] = OSSL_PARAM_construct_end();
219

220
        ret = EVP_PKEY_CTX_set_params(pkey_gen_ctx.get(), params);
221
        if (ret != OPENSSL_SUCCESS) {
222
                return expected::unexpected(MakeError(
223
                        SetupError,
224
                        "Failed to generate a private key. Parameters setting failed: "
225
                                + GetOpenSSLErrorMessage()));
226
        }
227

228
        EVP_PKEY *pkey = nullptr;
229
        ret = EVP_PKEY_generate(pkey_gen_ctx.get(), &pkey);
230
        if (ret != OPENSSL_SUCCESS) {
231
                return expected::unexpected(MakeError(
232
                        SetupError,
233
                        "Failed to generate a private key. Generation failed: " + GetOpenSSLErrorMessage()));
234
        }
235
#endif
236

237
        auto private_key = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(pkey, pkey_free_func);
8✔
238
        return unique_ptr<PrivateKey>(new PrivateKey(std::move(private_key)));
8✔
239
}
240

241
expected::ExpectedString EncodeBase64(vector<uint8_t> to_encode) {
18✔
242
        // Predict the len of the decoded for later verification. From man page:
243
        // For every 3 bytes of input provided 4 bytes of output
244
        // data will be produced. If n is not divisible by 3 (...)
245
        // the output is padded such that it is always divisible by 4.
246
        const uint64_t predicted_len {4 * ((to_encode.size() + 2) / 3)};
18✔
247

248
        // Add space for a NUL terminator. From man page:
249
        // Additionally a NUL terminator character will be added
250
        auto buffer {vector<unsigned char>(predicted_len + 1)};
18✔
251

252
        const int64_t output_len {
253
                EVP_EncodeBlock(buffer.data(), to_encode.data(), static_cast<int>(to_encode.size()))};
18✔
254
        assert(output_len >= 0);
255

256
        if (predicted_len != static_cast<uint64_t>(output_len)) {
18✔
257
                return expected::unexpected(
×
258
                        MakeError(Base64Error, "The predicted and the actual length differ"));
×
259
        }
260

261
        return string(buffer.begin(), buffer.end() - 1); // Remove the last zero byte
36✔
262
}
263

264
expected::ExpectedBytes DecodeBase64(string to_decode) {
13✔
265
        // Predict the len of the decoded for later verification. From man page:
266
        // For every 4 input bytes exactly 3 output bytes will be
267
        // produced. The output will be padded with 0 bits if necessary
268
        // to ensure that the output is always 3 bytes.
269
        const uint64_t predicted_len {3 * ((to_decode.size() + 3) / 4)};
13✔
270

271
        auto buffer {vector<unsigned char>(predicted_len)};
13✔
272

273
        const int64_t output_len {EVP_DecodeBlock(
13✔
274
                buffer.data(),
275
                common::ByteVectorFromString(to_decode).data(),
13✔
276
                static_cast<int>(to_decode.size()))};
13✔
277
        assert(output_len >= 0);
278

279
        if (predicted_len != static_cast<uint64_t>(output_len)) {
13✔
280
                return expected::unexpected(MakeError(
×
281
                        Base64Error,
282
                        "The predicted (" + std::to_string(predicted_len) + ") and the actual ("
×
283
                                + std::to_string(output_len) + ") length differ"));
×
284
        }
285

286
        // Subtract padding bytes. Inspired by internal OpenSSL code from:
287
        // https://github.com/openssl/openssl/blob/ff88545e02ab48a52952350c52013cf765455dd3/crypto/ct/ct_b64.c#L46
288
        for (auto it = to_decode.crbegin(); *it == '='; it++) {
18✔
289
                buffer.pop_back();
290
        }
291

292
        return buffer;
13✔
293
}
294

295

296
expected::ExpectedString ExtractPublicKey(const string &private_key_path) {
17✔
297
        auto private_bio_key = unique_ptr<BIO, void (*)(BIO *)>(
298
                BIO_new_file(private_key_path.c_str(), "r"), bio_free_func);
34✔
299

300
        if (!private_bio_key.get()) {
17✔
301
                return expected::unexpected(MakeError(
2✔
302
                        SetupError,
303
                        "Failed to open the private key file " + private_key_path + ": "
4✔
304
                                + GetOpenSSLErrorMessage()));
10✔
305
        }
306

307
        auto private_key = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
308
                PEM_read_bio_PrivateKey(private_bio_key.get(), nullptr, nullptr, nullptr), pkey_free_func);
30✔
309
        if (private_key == nullptr) {
15✔
310
                return expected::unexpected(MakeError(
×
311
                        SetupError,
312
                        "Failed to load the key (" + private_key_path + "):" + GetOpenSSLErrorMessage()));
×
313
        }
314

315
        auto bio_public_key = unique_ptr<BIO, void (*)(BIO *)>(BIO_new(BIO_s_mem()), bio_free_all_func);
30✔
316

317
        if (!bio_public_key.get()) {
15✔
318
                return expected::unexpected(MakeError(
×
319
                        SetupError,
320
                        "Failed to extract the public key from the private key " + private_key_path
×
321
                                + "):" + GetOpenSSLErrorMessage()));
×
322
        }
323

324
        int ret = PEM_write_bio_PUBKEY(bio_public_key.get(), private_key.get());
15✔
325
        if (ret != OPENSSL_SUCCESS) {
15✔
326
                return expected::unexpected(MakeError(
×
327
                        SetupError,
328
                        "Failed to extract the public key from: (" + private_key_path
×
329
                                + "): OpenSSL BIO write failed: " + GetOpenSSLErrorMessage()));
×
330
        }
331

332
        int pending = BIO_ctrl_pending(bio_public_key.get());
15✔
333
        if (pending <= 0) {
15✔
334
                return expected::unexpected(MakeError(
×
335
                        SetupError,
336
                        "Failed to extract the public key from: (" + private_key_path
×
337
                                + "): Zero byte key unexpected: " + GetOpenSSLErrorMessage()));
×
338
        }
339

340
        vector<uint8_t> key_vector(pending);
15✔
341

342
        size_t read = BIO_read(bio_public_key.get(), key_vector.data(), pending);
15✔
343

344
        if (read == 0) {
15✔
345
                MakeError(
×
346
                        SetupError,
347
                        "Failed to extract the public key from (" + private_key_path
×
348
                                + "): Zero bytes read from BIO: " + GetOpenSSLErrorMessage());
×
349
        }
350

351
        return string(key_vector.begin(), key_vector.end());
30✔
352
}
353

354
expected::ExpectedBytes SignData(const string &private_key_path, const vector<uint8_t> &digest) {
18✔
355
        auto bio_private_key = unique_ptr<BIO, void (*)(BIO *)>(
356
                BIO_new_file(private_key_path.c_str(), "r"), bio_free_func);
36✔
357
        if (bio_private_key == nullptr) {
18✔
358
                return expected::unexpected(MakeError(
1✔
359
                        SetupError,
360
                        "Failed to open the private key file (" + private_key_path
2✔
361
                                + "):" + GetOpenSSLErrorMessage()));
5✔
362
        }
363

364
        auto pkey = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
365
                PEM_read_bio_PrivateKey(bio_private_key.get(), nullptr, nullptr, nullptr), pkey_free_func);
34✔
366
        if (pkey == nullptr) {
17✔
367
                return expected::unexpected(MakeError(
×
368
                        SetupError,
369
                        "Failed to load the key from (" + private_key_path + "):" + GetOpenSSLErrorMessage()));
×
370
        }
371

372
        auto pkey_signer_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
373
                EVP_PKEY_CTX_new(pkey.get(), nullptr), pkey_ctx_free_func);
34✔
374

375
        if (EVP_PKEY_sign_init(pkey_signer_ctx.get()) <= 0) {
17✔
376
                return expected::unexpected(MakeError(
×
377
                        SetupError, "Failed to initialize the OpenSSL signer: " + GetOpenSSLErrorMessage()));
×
378
        }
379
        if (EVP_PKEY_CTX_set_signature_md(pkey_signer_ctx.get(), EVP_sha256()) <= 0) {
17✔
380
                return expected::unexpected(MakeError(
×
381
                        SetupError,
382
                        "Failed to set the OpenSSL signature to sha256: " + GetOpenSSLErrorMessage()));
×
383
        }
384

385
        vector<uint8_t> signature {};
386

387
        // Set the needed signature buffer length
388
        size_t digestlength = MENDER_DIGEST_SHA256_LENGTH, siglength;
389
        if (EVP_PKEY_sign(pkey_signer_ctx.get(), nullptr, &siglength, digest.data(), digestlength)
17✔
390
                <= 0) {
391
                return expected::unexpected(MakeError(
×
392
                        SetupError, "Failed to get the signature buffer length: " + GetOpenSSLErrorMessage()));
×
393
        }
394
        signature.resize(siglength);
17✔
395

396
        if (EVP_PKEY_sign(
17✔
397
                        pkey_signer_ctx.get(), signature.data(), &siglength, digest.data(), digestlength)
398
                <= 0) {
399
                return expected::unexpected(
×
400
                        MakeError(SetupError, "Failed to sign the digest: " + GetOpenSSLErrorMessage()));
×
401
        }
402

403
        // The signature may in some cases be shorter than the previously allocated
404
        // length (which is the max)
405
        signature.resize(siglength);
17✔
406

407
        return signature;
17✔
408
}
409

410
expected::ExpectedString Sign(const string &private_key_path, const mender::sha::SHA &shasum) {
18✔
411
        auto exp_signed_data = SignData(private_key_path, shasum);
36✔
412
        if (!exp_signed_data) {
18✔
413
                return expected::unexpected(exp_signed_data.error());
2✔
414
        }
415
        vector<uint8_t> signature = exp_signed_data.value();
17✔
416

417
        return EncodeBase64(signature);
34✔
418
}
419

420
expected::ExpectedString SignRawData(
17✔
421
        const string &private_key_path, const vector<uint8_t> &raw_data) {
422
        auto exp_shasum = mender::sha::Shasum(raw_data);
17✔
423

424
        if (!exp_shasum) {
17✔
425
                return expected::unexpected(exp_shasum.error());
×
426
        }
427
        auto shasum = exp_shasum.value();
17✔
428
        log::Debug("Shasum is: " + shasum.String());
34✔
429

430
        return Sign(private_key_path, shasum);
17✔
431
}
432

433
expected::ExpectedBool VerifySignData(
12✔
434
        const string &public_key_path,
435
        const mender::sha::SHA &shasum,
436
        const vector<uint8_t> &signature) {
437
        auto bio_key =
438
                unique_ptr<BIO, void (*)(BIO *)>(BIO_new_file(public_key_path.c_str(), "r"), bio_free_func);
24✔
439
        if (bio_key == nullptr) {
12✔
440
                return expected::unexpected(MakeError(
3✔
441
                        SetupError,
442
                        "Failed to open the public key file from (" + public_key_path
6✔
443
                                + "):" + GetOpenSSLErrorMessage()));
15✔
444
        }
445

446
        auto pkey = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
447
                PEM_read_bio_PUBKEY(bio_key.get(), nullptr, nullptr, nullptr), pkey_free_func);
18✔
448
        if (pkey == nullptr) {
9✔
449
                return expected::unexpected(MakeError(
2✔
450
                        SetupError,
451
                        "Failed to load the public key from(" + public_key_path
4✔
452
                                + "): " + GetOpenSSLErrorMessage()));
10✔
453
        }
454

455
        // prepare context
456
        auto pkey_signer_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
457
                EVP_PKEY_CTX_new(pkey.get(), nullptr), pkey_ctx_free_func);
14✔
458

459
        auto ret = EVP_PKEY_verify_init(pkey_signer_ctx.get());
7✔
460
        if (ret <= 0) {
7✔
461
                return expected::unexpected(MakeError(
×
462
                        SetupError, "Failed to initialize the OpenSSL signer: " + GetOpenSSLErrorMessage()));
×
463
        }
464
        ret = EVP_PKEY_CTX_set_signature_md(pkey_signer_ctx.get(), EVP_sha256());
7✔
465
        if (ret <= 0) {
7✔
466
                return expected::unexpected(MakeError(
×
467
                        SetupError,
468
                        "Failed to set the OpenSSL signature to sha256: " + GetOpenSSLErrorMessage()));
×
469
        }
470

471
        // verify signature
472
        ret = EVP_PKEY_verify(
7✔
473
                pkey_signer_ctx.get(), signature.data(), signature.size(), shasum.data(), shasum.size());
474
        if (ret < 0) {
7✔
475
                return expected::unexpected(MakeError(
×
476
                        VerificationError,
477
                        "Failed to verify signature. OpenSSL PKEY verify failed: " + GetOpenSSLErrorMessage()));
×
478
        }
479

480
        return ret == OPENSSL_SUCCESS;
7✔
481
}
482

483
expected::ExpectedBool VerifySign(
12✔
484
        const string &public_key_path, const mender::sha::SHA &shasum, const string &signature) {
485
        // signature: decode base64
486
        auto exp_decoded_signature = DecodeBase64(signature);
24✔
487
        if (!exp_decoded_signature) {
12✔
488
                return expected::unexpected(exp_decoded_signature.error());
×
489
        }
490
        auto decoded_signature = exp_decoded_signature.value();
12✔
491

492
        return VerifySignData(public_key_path, shasum, decoded_signature);
12✔
493
}
494

495
error::Error PrivateKey::SaveToPEM(const string &private_key_path) {
6✔
496
        auto bio_key = unique_ptr<BIO, void (*)(BIO *)>(
497
                BIO_new_file(private_key_path.c_str(), "w"), bio_free_func);
12✔
498
        if (bio_key == nullptr) {
6✔
499
                return MakeError(
500
                        SetupError,
501
                        "Failed to open the private key file (" + private_key_path
2✔
502
                                + "): " + GetOpenSSLErrorMessage());
4✔
503
        }
504

505
        // PEM_write_bio_PrivateKey_traditional will use the key-specific PKCS1
506
        // format if one is available for that key type, otherwise it will encode
507
        // to a PKCS8 key.
508
        auto ret = PEM_write_bio_PrivateKey_traditional(
5✔
509
                bio_key.get(), key.get(), nullptr, nullptr, 0, nullptr, nullptr);
510
        if (ret != OPENSSL_SUCCESS) {
5✔
511
                return MakeError(
512
                        SetupError,
513
                        "Failed to save the private key to file (" + private_key_path
×
514
                                + "): " + GetOpenSSLErrorMessage());
×
515
        }
516

517
        return error::NoError;
5✔
518
}
519

520
} // namespace crypto
521
} // namespace common
522
} // 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