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

mendersoftware / mender / 1047260214

24 Oct 2023 08:50AM UTC coverage: 80.226% (-0.1%) from 80.34%
1047260214

push

gitlab-ci

oleorhagen
test(artifact): Add signature verification of the test artifact for RSA

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

6873 of 8567 relevant lines covered (80.23%)

9388.93 hits per line

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

71.94
/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) {
27✔
54
        if (ctx) {
27✔
55
                EVP_PKEY_CTX_free(ctx);
27✔
56
        }
57
};
27✔
58
auto pkey_free_func = [](EVP_PKEY *key) {
40✔
59
        if (key) {
40✔
60
                EVP_PKEY_free(key);
40✔
61
        }
62
};
40✔
63
auto bio_free_func = [](BIO *bio) {
43✔
64
        if (bio) {
43✔
65
                BIO_free(bio);
43✔
66
        }
67
};
43✔
68
auto bio_free_all_func = [](BIO *bio) {
6✔
69
        if (bio) {
6✔
70
                BIO_free_all(bio);
6✔
71
        }
72
};
6✔
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) {
9✔
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)};
9✔
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)};
9✔
253

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

258
        if (predicted_len != static_cast<uint64_t>(output_len)) {
9✔
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
18✔
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++) {
23✔
291
                buffer.pop_back();
292
        }
293

294
        return buffer;
15✔
295
}
296

297

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

302
        if (!private_bio_key.get()) {
7✔
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);
12✔
311
        if (private_key == nullptr) {
6✔
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);
12✔
318

319
        if (!bio_public_key.get()) {
6✔
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());
6✔
327
        if (ret != OPENSSL_SUCCESS) {
6✔
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());
6✔
335
        if (pending <= 0) {
6✔
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);
6✔
343

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

346
        if (read == 0) {
6✔
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());
12✔
354
}
355

356
expected::ExpectedBytes SignData(const string &private_key_path, const vector<uint8_t> &digest) {
9✔
357
        auto bio_private_key = unique_ptr<BIO, void (*)(BIO *)>(
358
                BIO_new_file(private_key_path.c_str(), "r"), bio_free_func);
18✔
359
        if (bio_private_key == nullptr) {
9✔
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);
16✔
368
        if (pkey == nullptr) {
8✔
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);
16✔
376

377
        if (EVP_PKEY_sign_init(pkey_signer_ctx.get()) <= 0) {
8✔
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) {
8✔
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)
8✔
392
                <= 0) {
393
                return expected::unexpected(MakeError(
×
394
                        SetupError, "Failed to get the signature buffer length: " + GetOpenSSLErrorMessage()));
×
395
        }
396
        signature.resize(siglength);
8✔
397

398
        if (EVP_PKEY_sign(
8✔
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);
8✔
408

409
        return signature;
8✔
410
}
411

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

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

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

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

432
        return Sign(private_key_path, shasum);
8✔
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