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

mendersoftware / mender / 1000821593

12 Sep 2023 09:08AM UTC coverage: 78.672% (-0.5%) from 79.215%
1000821593

push

gitlab-ci

lluiscampos
fix(conf): Fix private key path to be set after changing `data_store`

Amends fix 4620c8456.

Changelog: None
Ticket: None

Signed-off-by: Lluis Campos <lluis.campos@northern.tech>

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

5843 of 7427 relevant lines covered (78.67%)

250.17 hits per line

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

77.0
/common/crypto/platform/openssl/crypto.cpp
1
// Copyright 2023 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
#include <common/crypto.hpp>
16

17
#include <cstdint>
18
#include <string>
19
#include <vector>
20
#include <memory>
21

22
#include <common/io.hpp>
23
#include <common/error.hpp>
24
#include <common/expected.hpp>
25
#include <common/common.hpp>
26
#include <common/crypto/platform/openssl/openssl_config.h>
27

28
#include <artifact/sha/sha.hpp>
29

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

36

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

41
const size_t MENDER_DIGEST_SHA256_LENGTH = 32;
42

43
const size_t OPENSSL_SUCCESS = 1;
44

45
using namespace std;
46

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

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

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

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

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

114
        vector<char> chars(passphrase.begin(), passphrase.end());
22✔
115
        chars.push_back('\0');
11✔
116
        char *c_str = chars.data();
11✔
117

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

125
                if (u == nullptr) {
3✔
126
                        return 0;
×
127
                }
128

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

133
                const int len = static_cast<int>(strlen(pass));
3✔
134
                return (len < size) ? len : size;
3✔
135
        };
136

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

144
        return unique_ptr<PrivateKey>(new PrivateKey(std::move(private_key)));
14✔
145
}
146

147
ExpectedPrivateKey PrivateKey::LoadFromPEM(const string &private_key_path) {
6✔
148
        return PrivateKey::LoadFromPEM(private_key_path, "");
12✔
149
}
150

151
ExpectedPrivateKey PrivateKey::Generate(const unsigned int bits, const unsigned int exponent) {
7✔
152
#ifdef MENDER_CRYPTO_OPENSSL_LEGACY
153
        auto pkey_gen_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
154
                EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr), pkey_ctx_free_func);
14✔
155

156
        int ret = EVP_PKEY_keygen_init(pkey_gen_ctx.get());
7✔
157
        if (ret != OPENSSL_SUCCESS) {
7✔
158
                return expected::unexpected(MakeError(
×
159
                        SetupError,
160
                        "Failed to generate a private key. Initialization failed: "
161
                                + GetOpenSSLErrorMessage()));
×
162
        }
163

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

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

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

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

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

209
        OSSL_PARAM params[3];
210
        auto bits_buffer = bits;
211
        auto exponent_buffer = exponent;
212
        params[0] = OSSL_PARAM_construct_uint("bits", &bits_buffer);
213
        params[1] = OSSL_PARAM_construct_uint("e", &exponent_buffer);
214
        params[2] = OSSL_PARAM_construct_end();
215

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

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

233
        auto private_key = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(pkey, pkey_free_func);
7✔
234
        return unique_ptr<PrivateKey>(new PrivateKey(std::move(private_key)));
14✔
235
}
236

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

244
        // Add space for a NUL terminator. From man page:
245
        // Additionally a NUL terminator character will be added
246
        auto buffer {vector<unsigned char>(predicted_len + 1)};
34✔
247

248
        const auto output_len =
249
                EVP_EncodeBlock(buffer.data(), to_encode.data(), static_cast<int>(to_encode.size()));
17✔
250

251
        if (predicted_len != static_cast<unsigned long>(output_len)) {
17✔
252
                return expected::unexpected(
×
253
                        MakeError(Base64Error, "The predicted and the actual length differ"));
×
254
        }
255

256
        return string(buffer.begin(), buffer.end() - 1); // Remove the last zero byte
34✔
257
}
258

259
expected::ExpectedBytes DecodeBase64(string to_decode) {
13✔
260
        // Predict the len of the decoded for later verification. From man page:
261
        // For every 4 input bytes exactly 3 output bytes will be
262
        // produced. The output will be padded with 0 bits if necessary
263
        // to ensure that the output is always 3 bytes.
264
        const auto predicted_len = 3 * ((to_decode.size() + 3) / 4);
13✔
265

266
        auto buffer {vector<unsigned char>(predicted_len)};
26✔
267

268
        const auto output_len = EVP_DecodeBlock(
13✔
269
                buffer.data(),
270
                common::ByteVectorFromString(to_decode).data(),
26✔
271
                static_cast<int>(to_decode.size()));
13✔
272

273
        if (predicted_len != static_cast<unsigned long>(output_len)) {
13✔
274
                return expected::unexpected(MakeError(
×
275
                        Base64Error,
276
                        "The predicted (" + std::to_string(predicted_len) + ") and the actual ("
×
277
                                + std::to_string(output_len) + ") length differ"));
×
278
        }
279

280
        // Subtract padding bytes. Inspired by internal OpenSSL code from:
281
        // https://github.com/openssl/openssl/blob/ff88545e02ab48a52952350c52013cf765455dd3/crypto/ct/ct_b64.c#L46
282
        for (auto it = to_decode.crbegin(); *it == '='; it++) {
18✔
283
                buffer.pop_back();
5✔
284
        }
285

286
        return buffer;
13✔
287
}
288

289

290
expected::ExpectedString ExtractPublicKey(const string &private_key_path) {
16✔
291
        auto private_bio_key = unique_ptr<BIO, void (*)(BIO *)>(
292
                BIO_new_file(private_key_path.c_str(), "r"), bio_free_func);
32✔
293

294
        if (!private_bio_key.get()) {
16✔
295
                return expected::unexpected(MakeError(
2✔
296
                        SetupError, "Failed to open the private key file: " + GetOpenSSLErrorMessage()));
4✔
297
        }
298

299
        auto private_key = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
300
                PEM_read_bio_PrivateKey(private_bio_key.get(), nullptr, nullptr, nullptr), pkey_free_func);
28✔
301
        if (private_key == nullptr) {
14✔
302
                return expected::unexpected(
×
303
                        MakeError(SetupError, "Failed to load the key: " + GetOpenSSLErrorMessage()));
×
304
        }
305

306
        auto bio_public_key = unique_ptr<BIO, void (*)(BIO *)>(BIO_new(BIO_s_mem()), bio_free_all_func);
28✔
307

308
        if (!bio_public_key.get()) {
14✔
309
                return expected::unexpected(MakeError(
×
310
                        SetupError,
311
                        "Failed to extract the public key from the private key: " + GetOpenSSLErrorMessage()));
×
312
        }
313

314
        int ret = PEM_write_bio_PUBKEY(bio_public_key.get(), private_key.get());
14✔
315
        if (ret != OPENSSL_SUCCESS) {
14✔
316
                return expected::unexpected(MakeError(
×
317
                        SetupError,
318
                        "Failed to extract the public key. OpenSSL BIO write failed: "
319
                                + GetOpenSSLErrorMessage()));
×
320
        }
321

322
        int pending = BIO_ctrl_pending(bio_public_key.get());
14✔
323
        if (pending <= 0) {
14✔
324
                return expected::unexpected(MakeError(
×
325
                        SetupError,
326
                        "Failed to extract the public key. Zero byte key unexpected: "
327
                                + GetOpenSSLErrorMessage()));
×
328
        }
329

330
        vector<uint8_t> key_vector(pending);
28✔
331

332
        size_t read = BIO_read(bio_public_key.get(), key_vector.data(), pending);
14✔
333

334
        if (read <= 0) {
14✔
335
                MakeError(
×
336
                        SetupError,
337
                        "Failed to extract the public key. Zero bytes read from BIO: "
338
                                + GetOpenSSLErrorMessage());
×
339
        }
340

341
        return string(key_vector.begin(), key_vector.end());
28✔
342
}
343

344
expected::ExpectedBytes SignData(const string private_key_path, const vector<uint8_t> digest) {
17✔
345
        auto bio_private_key = unique_ptr<BIO, void (*)(BIO *)>(
346
                BIO_new_file(private_key_path.c_str(), "r"), bio_free_func);
34✔
347
        if (bio_private_key == nullptr) {
17✔
348
                return expected::unexpected(MakeError(
1✔
349
                        SetupError, "Failed to open the private key file: " + GetOpenSSLErrorMessage()));
2✔
350
        }
351

352
        auto pkey = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
353
                PEM_read_bio_PrivateKey(bio_private_key.get(), nullptr, nullptr, nullptr), pkey_free_func);
32✔
354
        if (pkey == nullptr) {
16✔
355
                return expected::unexpected(
×
356
                        MakeError(SetupError, "Failed to load the key: " + GetOpenSSLErrorMessage()));
×
357
        }
358

359
        auto pkey_signer_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
360
                EVP_PKEY_CTX_new(pkey.get(), nullptr), pkey_ctx_free_func);
32✔
361

362
        if (EVP_PKEY_sign_init(pkey_signer_ctx.get()) <= 0) {
16✔
363
                return expected::unexpected(MakeError(
×
364
                        SetupError, "Failed to initialize the OpenSSL signer: " + GetOpenSSLErrorMessage()));
×
365
        }
366
        if (EVP_PKEY_CTX_set_signature_md(pkey_signer_ctx.get(), EVP_sha256()) <= 0) {
16✔
367
                return expected::unexpected(MakeError(
×
368
                        SetupError,
369
                        "Failed to set the OpenSSL signature to sha256: " + GetOpenSSLErrorMessage()));
×
370
        }
371

372
        vector<uint8_t> signature {};
32✔
373

374
        // Set the needed signature buffer length
375
        size_t digestlength = MENDER_DIGEST_SHA256_LENGTH, siglength;
16✔
376
        if (EVP_PKEY_sign(pkey_signer_ctx.get(), nullptr, &siglength, digest.data(), digestlength)
16✔
377
                <= 0) {
16✔
378
                return expected::unexpected(MakeError(
×
379
                        SetupError, "Failed to get the signature buffer length: " + GetOpenSSLErrorMessage()));
×
380
        }
381
        signature.resize(siglength);
16✔
382

383
        if (EVP_PKEY_sign(
16✔
384
                        pkey_signer_ctx.get(), signature.data(), &siglength, digest.data(), digestlength)
385
                <= 0) {
16✔
386
                return expected::unexpected(
×
387
                        MakeError(SetupError, "Failed to sign the digest: " + GetOpenSSLErrorMessage()));
×
388
        }
389

390
        // The signature may in some cases be shorter than the previously allocated
391
        // length (which is the max)
392
        signature.resize(siglength);
16✔
393

394
        return signature;
16✔
395
}
396

397
expected::ExpectedString Sign(const string &private_key_path, const mender::sha::SHA &shasum) {
17✔
398
        auto exp_signed_data = SignData(private_key_path, shasum);
51✔
399
        if (!exp_signed_data) {
17✔
400
                return expected::unexpected(exp_signed_data.error());
2✔
401
        }
402
        vector<uint8_t> signature = exp_signed_data.value();
16✔
403

404
        return EncodeBase64(signature);
16✔
405
}
406

407
expected::ExpectedString SignRawData(
16✔
408
        const string &private_key_path, const vector<uint8_t> &raw_data) {
409
        auto exp_shasum = mender::sha::Shasum(raw_data);
32✔
410

411
        if (!exp_shasum) {
16✔
412
                return expected::unexpected(exp_shasum.error());
×
413
        }
414
        auto shasum = exp_shasum.value();
32✔
415
        log::Debug("Shasum is: " + shasum.String());
16✔
416

417
        return Sign(private_key_path, shasum);
16✔
418
}
419

420
expected::ExpectedBool VerifySignData(
12✔
421
        const string &public_key_path,
422
        const mender::sha::SHA &shasum,
423
        const vector<uint8_t> &signature) {
424
        auto bio_key =
425
                unique_ptr<BIO, void (*)(BIO *)>(BIO_new_file(public_key_path.c_str(), "r"), bio_free_func);
24✔
426
        if (bio_key == nullptr) {
12✔
427
                return expected::unexpected(MakeError(
3✔
428
                        SetupError, "Failed to open the public key file: " + GetOpenSSLErrorMessage()));
6✔
429
        }
430

431
        auto pkey = unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)>(
432
                PEM_read_bio_PUBKEY(bio_key.get(), nullptr, nullptr, nullptr), pkey_free_func);
18✔
433
        if (pkey == nullptr) {
9✔
434
                return expected::unexpected(
2✔
435
                        MakeError(SetupError, "Failed to load the key: " + GetOpenSSLErrorMessage()));
4✔
436
        }
437

438
        // prepare context
439
        auto pkey_signer_ctx = unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX *)>(
440
                EVP_PKEY_CTX_new(pkey.get(), nullptr), pkey_ctx_free_func);
14✔
441

442
        auto ret = EVP_PKEY_verify_init(pkey_signer_ctx.get());
7✔
443
        if (ret <= 0) {
7✔
444
                return expected::unexpected(MakeError(
×
445
                        SetupError, "Failed to initialize the OpenSSL signer: " + GetOpenSSLErrorMessage()));
×
446
        }
447
        ret = EVP_PKEY_CTX_set_signature_md(pkey_signer_ctx.get(), EVP_sha256());
7✔
448
        if (ret <= 0) {
7✔
449
                return expected::unexpected(MakeError(
×
450
                        SetupError,
451
                        "Failed to set the OpenSSL signature to sha256: " + GetOpenSSLErrorMessage()));
×
452
        }
453

454
        // verify signature
455
        ret = EVP_PKEY_verify(
7✔
456
                pkey_signer_ctx.get(), signature.data(), signature.size(), shasum.data(), shasum.size());
457
        if (ret < 0) {
7✔
458
                return expected::unexpected(MakeError(
×
459
                        VerificationError,
460
                        "Failed to verify signature. OpenSSL PKEY verify failed: " + GetOpenSSLErrorMessage()));
×
461
        }
462

463
        return ret == OPENSSL_SUCCESS;
14✔
464
}
465

466
expected::ExpectedBool VerifySign(
12✔
467
        const string &public_key_path, const mender::sha::SHA &shasum, const string &signature) {
468
        // signature: decode base64
469
        auto exp_decoded_signature = DecodeBase64(signature);
24✔
470
        if (!exp_decoded_signature) {
12✔
471
                return expected::unexpected(exp_decoded_signature.error());
×
472
        }
473
        auto decoded_signature = exp_decoded_signature.value();
24✔
474

475
        return VerifySignData(public_key_path, shasum, decoded_signature);
12✔
476
}
477

478
error::Error PrivateKey::SaveToPEM(const string &private_key_path) {
5✔
479
        auto bio_key = unique_ptr<BIO, void (*)(BIO *)>(
480
                BIO_new_file(private_key_path.c_str(), "w"), bio_free_func);
10✔
481
        if (bio_key == nullptr) {
5✔
482
                return MakeError(
483
                        SetupError, "Failed to open the private key file: " + GetOpenSSLErrorMessage());
2✔
484
        }
485

486
        // PEM_write_bio_PrivateKey_traditional will use the key-specific PKCS1
487
        // format if one is available for that key type, otherwise it will encode
488
        // to a PKCS8 key.
489
        auto ret = PEM_write_bio_PrivateKey_traditional(
4✔
490
                bio_key.get(), key.get(), nullptr, nullptr, 0, nullptr, nullptr);
491
        if (ret != OPENSSL_SUCCESS) {
4✔
492
                return MakeError(
493
                        SetupError, "Failed to save the private key to file: " + GetOpenSSLErrorMessage());
×
494
        }
495

496
        return error::NoError;
4✔
497
}
498

499
} // namespace crypto
500
} // namespace common
501
} // 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