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

mendersoftware / mender / 1056184062

31 Oct 2023 12:41PM UTC coverage: 80.182% (+0.3%) from 79.877%
1056184062

push

gitlab-ci

kacf
chore: Do not try to resume requests that were cancelled by callers.

This requires us to move the destruction of the inner reader until
after the body handler has been called, otherwise it will trigger
another `operation_canceled` error which we don't want.

Ticket: MEN-6807

Signed-off-by: Kristian Amlie <kristian.amlie@northern.tech>

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

6890 of 8593 relevant lines covered (80.18%)

9363.89 hits per line

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

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

25
#include <openssl/bn.h>
26
#include <openssl/ecdsa.h>
27
#include <openssl/err.h>
28
#include <openssl/evp.h>
29
#include <openssl/pem.h>
30
#include <openssl/rsa.h>
31

32
#include <common/io.hpp>
33
#include <common/error.hpp>
34
#include <common/expected.hpp>
35
#include <common/common.hpp>
36

37
#include <artifact/sha/sha.hpp>
38

39

40
namespace mender {
41
namespace common {
42
namespace crypto {
43

44
const size_t MENDER_DIGEST_SHA256_LENGTH = 32;
45

46
const size_t OPENSSL_SUCCESS = 1;
47

48
using namespace std;
49

50
namespace error = mender::common::error;
51
namespace io = mender::common::io;
52

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

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

89
        std::string errorDescription {};
90
        while (sslErrorCode != 0) {
56✔
91
                if (!errorDescription.empty()) {
39✔
92
                        errorDescription += '\n';
93
                }
94
                errorDescription += ERR_error_string(sslErrorCode, nullptr);
39✔
95
                sslErrorCode = ERR_get_error();
39✔
96
        }
97
        if (sysErrorCode != 0) {
17✔
98
                if (!errorDescription.empty()) {
15✔
99
                        errorDescription += '\n';
100
                }
101
                errorDescription += "System error, code=" + std::to_string(sysErrorCode);
30✔
102
                errorDescription += ", ";
15✔
103
                errorDescription += strerror(sysErrorCode);
15✔
104
        }
105
        return errorDescription;
17✔
106
}
107

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

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

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

130
                if (u == nullptr) {
3✔
131
                        return 0;
132
                }
133

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

263
        return string(buffer.begin(), buffer.end() - 1); // Remove the last zero byte
20✔
264
}
265

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

273
        auto buffer {vector<unsigned char>(predicted_len)};
15✔
274

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

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

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

294
        return buffer;
15✔
295
}
296

297

298
expected::ExpectedString ExtractPublicKey(const string &private_key_path) {
8✔
299
        auto private_bio_key = unique_ptr<BIO, void (*)(BIO *)>(
300
                BIO_new_file(private_key_path.c_str(), "r"), bio_free_func);
16✔
301

302
        if (!private_bio_key.get()) {
8✔
303
                return expected::unexpected(MakeError(
1✔
304
                        SetupError,
305
                        "Failed to open the private key file " + private_key_path + ": "
2✔
306
                                + GetOpenSSLErrorMessage()));
5✔
307
        }
308

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

317
        auto bio_public_key = unique_ptr<BIO, void (*)(BIO *)>(BIO_new(BIO_s_mem()), bio_free_all_func);
14✔
318

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

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

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

342
        vector<uint8_t> key_vector(pending);
7✔
343

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

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

353
        return string(key_vector.begin(), key_vector.end());
14✔
354
}
355

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

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

374
        auto pkey_signer_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
375
                EVP_PKEY_CTX_new(pkey.get(), nullptr), pkey_ctx_free_func);
18✔
376

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

387
        vector<uint8_t> signature {};
388

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

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

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

409
        return signature;
9✔
410
}
411

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

419
        return EncodeBase64(signature);
18✔
420
}
421

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

426
        if (!exp_shasum) {
9✔
427
                return expected::unexpected(exp_shasum.error());
×
428
        }
429
        auto shasum = exp_shasum.value();
9✔
430
        log::Debug("Shasum is: " + shasum.String());
18✔
431

432
        return Sign(private_key_path, shasum);
9✔
433
}
434

435
const size_t mender_decode_buf_size = 256;
436
const size_t ecdsa256keySize = 32;
437

438
// Try and decode the keys from pure binary, assuming that the points on the
439
// curve (r,s), have been concatenated together (r || s), and simply dumped to
440
// binary. Which is what we did in the `mender-artifact` tool.
441
// (See MEN-1740) for some insight into previous issues, and the chosen fix.
442
static expected::ExpectedBytes TryASN1EncodeMenderCustomBinaryECFormat(
2✔
443
        const vector<uint8_t> &signature,
444
        const mender::sha::SHA &shasum,
445
        std::function<BIGNUM *(const unsigned char *signature, int length, BIGNUM *_unused)>
446
                BinaryDecoderFn) {
447
        // Verify that the marshalled keys match our expectation
448
        const size_t assumed_signature_size {2 * ecdsa256keySize};
449
        if (signature.size() > assumed_signature_size) {
2✔
450
                return expected::unexpected(MakeError(
×
451
                        SetupError,
452
                        "Unexpected size of the signature for ECDSA. Expected 2*" + to_string(ecdsa256keySize)
×
453
                                + " bytes. Got: " + to_string(signature.size())));
×
454
        }
455
        auto ecSig = unique_ptr<ECDSA_SIG, void (*)(ECDSA_SIG *)>(ECDSA_SIG_new(), ECDSA_SIG_free);
4✔
456
        if (ecSig == nullptr) {
2✔
457
                return expected::unexpected(MakeError(
×
458
                        SetupError,
459
                        "Failed to allocate the structure for the ECDSA signature: "
460
                                + GetOpenSSLErrorMessage()));
×
461
        }
462

463
        auto r = unique_ptr<BIGNUM, void (*)(BIGNUM *)>(
464
                BinaryDecoderFn(signature.data(), ecdsa256keySize, NULL /* allocate new memory for r */),
465
                BN_free);
4✔
466
        if (r == nullptr) {
2✔
467
                return expected::unexpected(MakeError(
×
468
                        SetupError,
469
                        "Failed to extract the r(andom) part from the ECDSA signature in the binary representation: "
470
                                + GetOpenSSLErrorMessage()));
×
471
        }
472
        auto s = unique_ptr<BIGNUM, void (*)(BIGNUM *)>(
473
                BinaryDecoderFn(
474
                        signature.data() + ecdsa256keySize,
475
                        ecdsa256keySize,
476
                        NULL /* allocate new memory for s */),
477
                BN_free);
4✔
478
        if (s == nullptr) {
2✔
479
                return expected::unexpected(MakeError(
×
480
                        SetupError,
481
                        "Failed to extract the s(ignature) part from the ECDSA signature in the binary representation: "
482
                                + GetOpenSSLErrorMessage()));
×
483
        }
484

485
        // Set the r&s values in the SIG struct
486
        // r & s now owned by ecSig
487
        int ret {ECDSA_SIG_set0(ecSig.get(), r.get(), s.get())};
2✔
488
        if (ret != OPENSSL_SUCCESS) {
2✔
489
                return expected::unexpected(MakeError(
×
490
                        SetupError,
491
                        "Failed to set the signature parts in the ECDSA structure: "
492
                                + GetOpenSSLErrorMessage()));
×
493
        }
494
        r.release();
495
        s.release();
496

497
        /* Allocate some array guaranteed to hold the DER-encoded structure */
498
        vector<uint8_t> der_encoded_byte_array(mender_decode_buf_size);
2✔
499
        unsigned char *arr_p = &der_encoded_byte_array[0];
2✔
500
        int len = i2d_ECDSA_SIG(ecSig.get(), &arr_p);
2✔
501
        if (len < 0) {
2✔
502
                return expected::unexpected(MakeError(
×
503
                        SetupError,
504
                        "Failed to set the signature parts in the ECDSA structure: "
505
                                + GetOpenSSLErrorMessage()));
×
506
        }
507
        /* Resize to the actual size of the DER-encoded signature */
508
        der_encoded_byte_array.resize(len);
2✔
509

510
        return der_encoded_byte_array;
2✔
511
}
512

513

514
expected::ExpectedBool VerifySignData(
515
        const string &public_key_path,
516
        const mender::sha::SHA &shasum,
517
        const vector<uint8_t> &signature);
518

519
static expected::ExpectedBool VerifyECDSASignData(
2✔
520
        const string &public_key_path,
521
        const mender::sha::SHA &shasum,
522
        const vector<uint8_t> &signature) {
523
        expected::ExpectedBytes exp_der_encoded_signature =
524
                TryASN1EncodeMenderCustomBinaryECFormat(signature, shasum, BN_bin2bn)
4✔
525
                        .or_else([&signature, &shasum](error::Error big_endian_error) {
×
526
                                log::Debug(
×
527
                                        "Failed to decode the signature binary blob from our custom binary format assuming the big-endian encoding, error: "
528
                                        + big_endian_error.String()
×
529
                                        + " falling back and trying anew assuming it is little-endian encoded: ");
×
530
                                return TryASN1EncodeMenderCustomBinaryECFormat(signature, shasum, BN_lebin2bn);
×
531
                        });
2✔
532
        if (!exp_der_encoded_signature) {
2✔
533
                return expected::unexpected(
×
534
                        MakeError(VerificationError, exp_der_encoded_signature.error().message));
×
535
        }
536

537
        vector<uint8_t> der_encoded_signature = exp_der_encoded_signature.value();
2✔
538

539
        return VerifySignData(public_key_path, shasum, der_encoded_signature);
2✔
540
}
541

542
static bool OpenSSLSignatureVerificationError(int a) {
543
        /*
544
         * The signature check errored. This is different from the signature being
545
         * wrong. We simply were not able to perform the check in this instance.
546
         * Therefore, we fall back to trying the custom marshalled binary ECDSA
547
         * signature, which we have been using in Mender.
548
         */
549
        return a < 0;
550
}
551

552
expected::ExpectedBool VerifySignData(
16✔
553
        const string &public_key_path,
554
        const mender::sha::SHA &shasum,
555
        const vector<uint8_t> &signature) {
556
        auto bio_key =
557
                unique_ptr<BIO, void (*)(BIO *)>(BIO_new_file(public_key_path.c_str(), "r"), bio_free_func);
32✔
558
        if (bio_key == nullptr) {
16✔
559
                return expected::unexpected(MakeError(
3✔
560
                        SetupError,
561
                        "Failed to open the public key file from (" + public_key_path
6✔
562
                                + "):" + GetOpenSSLErrorMessage()));
15✔
563
        }
564

565
        auto pkey = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
566
                PEM_read_bio_PUBKEY(bio_key.get(), nullptr, nullptr, nullptr), pkey_free_func);
26✔
567
        if (pkey == nullptr) {
13✔
568
                return expected::unexpected(MakeError(
2✔
569
                        SetupError,
570
                        "Failed to load the public key from(" + public_key_path
4✔
571
                                + "): " + GetOpenSSLErrorMessage()));
10✔
572
        }
573

574
        auto pkey_signer_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
575
                EVP_PKEY_CTX_new(pkey.get(), nullptr), pkey_ctx_free_func);
22✔
576

577
        auto ret = EVP_PKEY_verify_init(pkey_signer_ctx.get());
11✔
578
        if (ret <= 0) {
11✔
579
                return expected::unexpected(MakeError(
×
580
                        SetupError, "Failed to initialize the OpenSSL signer: " + GetOpenSSLErrorMessage()));
×
581
        }
582
        ret = EVP_PKEY_CTX_set_signature_md(pkey_signer_ctx.get(), EVP_sha256());
11✔
583
        if (ret <= 0) {
11✔
584
                return expected::unexpected(MakeError(
×
585
                        SetupError,
586
                        "Failed to set the OpenSSL signature to sha256: " + GetOpenSSLErrorMessage()));
×
587
        }
588

589
        // verify signature
590
        ret = EVP_PKEY_verify(
11✔
591
                pkey_signer_ctx.get(), signature.data(), signature.size(), shasum.data(), shasum.size());
592
        if (OpenSSLSignatureVerificationError(ret)) {
11✔
593
                log::Debug(
2✔
594
                        "Failed to verify the signature with the supported OpenSSL binary formats. Falling back to the custom Mender encoded binary format for ECDSA signatures: "
595
                        + GetOpenSSLErrorMessage());
4✔
596
                return VerifyECDSASignData(public_key_path, shasum, signature);
2✔
597
        }
598
        if (ret == OPENSSL_SUCCESS) {
9✔
599
                return true;
600
        }
601
        /* This is the case where ret == 0. The signature is simply wrong */
602
        return false;
603
}
604

605
expected::ExpectedBool VerifySign(
14✔
606
        const string &public_key_path, const mender::sha::SHA &shasum, const string &signature) {
607
        // signature: decode base64
608
        auto exp_decoded_signature = DecodeBase64(signature);
28✔
609
        if (!exp_decoded_signature) {
14✔
610
                return expected::unexpected(exp_decoded_signature.error());
×
611
        }
612
        auto decoded_signature = exp_decoded_signature.value();
14✔
613

614
        return VerifySignData(public_key_path, shasum, decoded_signature);
14✔
615
}
616

617
error::Error PrivateKey::SaveToPEM(const string &private_key_path) {
6✔
618
        auto bio_key = unique_ptr<BIO, void (*)(BIO *)>(
619
                BIO_new_file(private_key_path.c_str(), "w"), bio_free_func);
12✔
620
        if (bio_key == nullptr) {
6✔
621
                return MakeError(
622
                        SetupError,
623
                        "Failed to open the private key file (" + private_key_path
2✔
624
                                + "): " + GetOpenSSLErrorMessage());
4✔
625
        }
626

627
        // PEM_write_bio_PrivateKey_traditional will use the key-specific PKCS1
628
        // format if one is available for that key type, otherwise it will encode
629
        // to a PKCS8 key.
630
        auto ret = PEM_write_bio_PrivateKey_traditional(
5✔
631
                bio_key.get(), key.get(), nullptr, nullptr, 0, nullptr, nullptr);
632
        if (ret != OPENSSL_SUCCESS) {
5✔
633
                return MakeError(
634
                        SetupError,
635
                        "Failed to save the private key to file (" + private_key_path
×
636
                                + "): " + GetOpenSSLErrorMessage());
×
637
        }
638

639
        return error::NoError;
5✔
640
}
641

642
} // namespace crypto
643
} // namespace common
644
} // 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