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

mendersoftware / mender / 1046240096

23 Oct 2023 01:07PM UTC coverage: 80.229% (-0.1%) from 80.369%
1046240096

push

gitlab-ci

oleorhagen
fix(artifact): Add support for verifying our custom EC signature format

This adds support for verifying our custom EC signature marshalling from our
Mender-Artifact format/tool.

Somewhat surprisingly, it turns out that we do not encode the `EC` signature in
any known binary encoding format.

Instead we simply concatenate the integers (r & s) in this instance, into a
binary array, and write it to file.

This brings a host of issues obviously (where hard to understand is just one of
them). The other is that not encoding it in an architecture independent format
is that the signature will only be verified on the architecture it was made (it
needs the right byte-order), so Big- and Little-endian makes a difference here.

To accomodate this, the new client will try and decode both integers in both
Little- and Big-endian format, then encode it into `ASN.1` and `DER`, and then
pass it on to our regular verification code.

Ticket: MEN-6671
Changelog: None

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

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

6874 of 8568 relevant lines covered (80.23%)

9387.78 hits per line

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

72.05
/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 ecdsa256keySize = 32;
436

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

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

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

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

509
        return der_encoded_byte_array;
2✔
510
}
511

512

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

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

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

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

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

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

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

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

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

588
        // verify signature
589
        ret = EVP_PKEY_verify(
11✔
590
                pkey_signer_ctx.get(), signature.data(), signature.size(), shasum.data(), shasum.size());
591
        if (OpenSSLSignatureVerificationError(ret)) {
11✔
592
                const string openssl_error_msg {GetOpenSSLErrorMessage()};
2✔
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");
4✔
595
                return VerifyECDSASignData(public_key_path, shasum, signature);
2✔
596
                return expected::unexpected(MakeError(
597
                        VerificationError,
598
                        "Failed to verify the signature. OpenSSL PKEY verify failed: " + openssl_error_msg));
599
        }
600
        if (ret == OPENSSL_SUCCESS) {
9✔
601
                return true;
602
        }
603
        /* This is the case where ret == 0. The signature is simply wrong */
604
        return false;
605
}
606

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

616
        return VerifySignData(public_key_path, shasum, decoded_signature);
14✔
617
}
618

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

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

641
        return error::NoError;
5✔
642
}
643

644
} // namespace crypto
645
} // namespace common
646
} // 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