Coverage Report

Created: 2026-10-28 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bmcweb/include/ossl_wrappers.hpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright OpenBMC Authors
3
4
#pragma once
5
#include "logging.hpp"
6
7
#include <boost/asio/ssl/verify_context.hpp>
8
9
#include <algorithm>
10
#include <array>
11
#include <bit>
12
#include <cstdint>
13
#include <filesystem>
14
15
extern "C"
16
{
17
#include <openssl/asn1.h>
18
#include <openssl/bio.h>
19
#include <openssl/ec.h>
20
#include <openssl/err.h>
21
#include <openssl/evp.h>
22
#include <openssl/obj_mac.h>
23
#include <openssl/objects.h>
24
#include <openssl/pem.h>
25
#include <openssl/ssl.h>
26
#include <openssl/tls1.h>
27
#include <openssl/types.h>
28
#include <openssl/x509.h>
29
#include <openssl/x509_vfy.h>
30
#include <openssl/x509v3.h>
31
}
32
33
class OpenSSLEVPKey;
34
class OpenSSLX509;
35
36
class OpenSSLBIO
37
{
38
  public:
39
    friend OpenSSLEVPKey;
40
    friend OpenSSLX509;
41
0
    OpenSSLBIO() : ptr(BIO_new(BIO_s_mem())) {}
42
    OpenSSLBIO(std::string& data) :
43
0
        ptr(BIO_new_mem_buf(static_cast<void*>(data.data()),
44
0
                            static_cast<int>(data.size())))
45
0
    {}
46
    OpenSSLBIO(const std::filesystem::path& filepath) :
47
0
        ptr(BIO_new_file(filepath.c_str(), "rb"))
48
0
    {
49
0
        if (ptr == nullptr)
50
0
        {
51
0
            throw std::runtime_error("Failed to open file");
52
0
        }
53
0
    }
54
55
    OpenSSLBIO(const OpenSSLBIO&) = delete;
56
    OpenSSLBIO(OpenSSLBIO&& other) noexcept : ptr(other.ptr)
57
0
    {
58
0
        other.ptr = nullptr;
59
0
    }
60
    OpenSSLBIO& operator=(const OpenSSLBIO&) = delete;
61
    OpenSSLBIO& operator=(OpenSSLBIO&& other) noexcept
62
0
    {
63
0
        if (this == &other)
64
0
        {
65
0
            return *this;
66
0
        }
67
0
        BIO_free(ptr);
68
0
        ptr = other.ptr;
69
0
        other.ptr = nullptr;
70
0
        return *this;
71
0
    }
72
73
    ~OpenSSLBIO()
74
0
    {
75
0
        BIO_free(ptr);
76
0
    }
77
78
    std::string_view getMemData()
79
0
    {
80
0
        char* data = nullptr;
81
0
        std::string_view result;
82
0
        long dataLen = BIO_get_mem_data(ptr, &data);
83
0
        if (dataLen > 0)
84
0
        {
85
0
            result = std::string_view(data, static_cast<size_t>(dataLen));
86
0
        }
87
0
        return result;
88
0
    }
89
90
  private:
91
    BIO* get()
92
0
    {
93
0
        return ptr;
94
0
    }
95
96
    BIO* ptr;
97
};
98
99
class OpenSSLX509Extension
100
{
101
  public:
102
    friend OpenSSLX509;
103
104
    OpenSSLX509Extension(X509V3_CTX* ctx, int nid, std::string_view value)
105
0
    {
106
0
        std::string valueStr(value);
107
0
        ptr = X509V3_EXT_conf_nid(nullptr, ctx, nid, valueStr.c_str());
108
0
        if (ptr == nullptr)
109
0
        {
110
0
            throw std::runtime_error(
111
0
                "Error: In X509V3_EXT_conf_nid: " + std::string(value));
112
0
        }
113
0
    }
114
    OpenSSLX509Extension(const OpenSSLX509Extension&) = delete;
115
    OpenSSLX509Extension(OpenSSLX509Extension&& other) noexcept = delete;
116
    OpenSSLX509Extension& operator=(const OpenSSLX509Extension&) = delete;
117
    OpenSSLX509Extension& operator=(OpenSSLX509Extension&& other) noexcept =
118
        delete;
119
120
    ~OpenSSLX509Extension()
121
0
    {
122
0
        X509_EXTENSION_free(ptr);
123
0
    }
124
125
  private:
126
    X509_EXTENSION* get()
127
0
    {
128
0
        return ptr;
129
0
    }
130
131
    X509_EXTENSION* ptr;
132
};
133
134
class OpenSSLEVPKeyCTX;
135
class OpenSSLSSLCtx;
136
class OpenSSLSSL;
137
138
class OpenSSLEVPKey
139
{
140
  public:
141
    friend OpenSSLX509;
142
    friend OpenSSLEVPKeyCTX;
143
    friend OpenSSLSSLCtx;
144
    friend OpenSSLSSL;
145
146
0
    OpenSSLEVPKey() : ptr(EVP_PKEY_new()) {}
147
148
    static std::optional<OpenSSLEVPKey> readFromPrivateKey(std::string& data)
149
0
    {
150
0
        OpenSSLBIO bufio(data);
151
0
        EVP_PKEY* retPtr =
152
0
            PEM_read_bio_PrivateKey(bufio.get(), nullptr, nullptr, nullptr);
153
0
        if (retPtr == nullptr)
154
0
        {
155
0
            return std::nullopt;
156
0
        }
157
0
        return {OpenSSLEVPKey(retPtr)};
158
0
    }
159
160
    bool pemWriteBioPrivateKey(OpenSSLBIO& bufio) const
161
0
    {
162
0
        return PEM_write_bio_PrivateKey(bufio.get(), ptr, nullptr, nullptr, 0,
163
0
                                        nullptr, nullptr) > 0;
164
0
    }
165
166
    OpenSSLEVPKey(const OpenSSLEVPKey&) = delete;
167
0
    OpenSSLEVPKey(OpenSSLEVPKey&& other) noexcept : ptr(other.ptr)
168
0
    {
169
0
        other.ptr = nullptr;
170
0
    }
171
    OpenSSLEVPKey& operator=(const OpenSSLEVPKey&) = delete;
172
    OpenSSLEVPKey& operator=(OpenSSLEVPKey&& other) noexcept
173
0
    {
174
0
        if (this == &other)
175
0
        {
176
0
            return *this;
177
0
        }
178
0
        EVP_PKEY_free(ptr);
179
0
        ptr = other.ptr;
180
0
        other.ptr = nullptr;
181
0
        return *this;
182
0
    }
183
184
    static OpenSSLEVPKey fromKeygen(EVP_PKEY_CTX* pctx)
185
0
    {
186
0
        OpenSSLEVPKey key;
187
0
        if (EVP_PKEY_keygen(pctx, &key.ptr) != 1)
188
0
        {
189
0
            throw std::runtime_error("Failed to generate key");
190
0
        }
191
0
        return key;
192
0
    }
193
194
    ~OpenSSLEVPKey()
195
0
    {
196
0
        EVP_PKEY_free(ptr);
197
0
    }
198
199
  private:
200
    EVP_PKEY* get() const
201
0
    {
202
0
        return ptr;
203
0
    }
204
205
0
    OpenSSLEVPKey(EVP_PKEY* ptrIn) : ptr(ptrIn) {}
206
207
    EVP_PKEY* ptr;
208
};
209
210
class OpenSSLASN1String
211
{
212
  public:
213
    OpenSSLASN1String(std::string_view data, int type = V_ASN1_OCTET_STRING) :
214
        ptr(ASN1_STRING_type_new(type))
215
0
    {
216
0
        if (ptr == nullptr)
217
0
        {
218
0
            throw std::runtime_error("Failed to create ASN1_STRING");
219
0
        }
220
0
        if (ASN1_STRING_set(ptr, data.data(), static_cast<int>(data.size())) !=
221
0
            1)
222
0
        {
223
0
            throw std::runtime_error("Failed to set ASN1_STRING");
224
0
        }
225
0
    }
226
0
    OpenSSLASN1String(ASN1_STRING* ptrIn) : ptr(ptrIn) {}
227
228
    OpenSSLASN1String(const OpenSSLASN1String&) = delete;
229
    OpenSSLASN1String(OpenSSLASN1String&& other) noexcept : ptr(other.ptr)
230
0
    {
231
0
        other.ptr = nullptr;
232
0
    }
233
234
    OpenSSLASN1String& operator=(const OpenSSLASN1String&) = delete;
235
    OpenSSLASN1String& operator=(OpenSSLASN1String&& other) noexcept
236
0
    {
237
0
        if (this == &other)
238
0
        {
239
0
            return *this;
240
0
        }
241
0
        ASN1_STRING_free(ptr);
242
0
        ptr = other.ptr;
243
0
        other.ptr = nullptr;
244
0
        return *this;
245
0
    }
246
247
    ~OpenSSLASN1String()
248
0
    {
249
0
        ASN1_STRING_free(ptr);
250
0
    }
251
    std::string_view getAsString() const
252
0
    {
253
0
        return {std::bit_cast<const char*>(ASN1_STRING_get0_data(ptr)),
254
0
                static_cast<size_t>(ASN1_STRING_length(ptr))};
255
0
    }
256
257
    ASN1_STRING* release()
258
0
    {
259
0
        ASN1_STRING* temp = ptr;
260
0
        ptr = nullptr;
261
0
        return temp;
262
0
    }
263
264
  private:
265
    ASN1_STRING* get() const
266
0
    {
267
0
        return ptr;
268
0
    }
269
270
    ASN1_STRING* ptr;
271
};
272
273
class OpenSSLGeneralNames;
274
275
struct OpenSSLGeneralName
276
{
277
  public:
278
    friend OpenSSLGeneralNames;
279
0
    OpenSSLGeneralName() : ptr(GENERAL_NAME_new()) {}
280
281
    OpenSSLGeneralName(int type, std::string_view value) :
282
        ptr(GENERAL_NAME_new())
283
0
    {
284
0
        if (type >= 0 && type <= GEN_RID)
285
0
        {
286
0
            OpenSSLASN1String str(value);
287
0
            GENERAL_NAME_set0_value(ptr, type, str.release());
288
0
        }
289
0
        else
290
0
        {
291
0
            // type is a NID: build an otherName SAN with that OID
292
0
            OpenSSLASN1String str(value, V_ASN1_UTF8STRING);
293
0
            ASN1_OBJECT* oid = OBJ_dup(OBJ_nid2obj(type));
294
0
            ASN1_TYPE* atype = ASN1_TYPE_new();
295
0
            ASN1_TYPE_set(atype, V_ASN1_UTF8STRING, str.release());
296
0
            GENERAL_NAME_set0_othername(ptr, oid, atype);
297
0
        }
298
0
    }
299
300
    OpenSSLGeneralName(const OpenSSLGeneralName&) = delete;
301
    OpenSSLGeneralName(OpenSSLGeneralName&& other) noexcept = delete;
302
    OpenSSLGeneralName& operator=(const OpenSSLGeneralName&) = delete;
303
    OpenSSLGeneralName& operator=(OpenSSLGeneralName&& other) noexcept = delete;
304
305
    GENERAL_NAME* release()
306
0
    {
307
0
        GENERAL_NAME* temp = ptr;
308
0
        ptr = nullptr;
309
0
        return temp;
310
0
    }
311
312
    ~OpenSSLGeneralName()
313
0
    {
314
0
        GENERAL_NAME_free(ptr);
315
0
    }
316
317
  private:
318
    GENERAL_NAME* ptr;
319
};
320
321
class OpenSSLGeneralNames
322
{
323
  private:
324
0
    OpenSSLGeneralNames(GENERAL_NAMES* ptrIn) : ptr(ptrIn) {}
325
326
  public:
327
    friend OpenSSLX509;
328
0
    OpenSSLGeneralNames() : ptr(GENERAL_NAMES_new()) {}
329
330
    // This parameter isn't "moved" but it is released, so clang-tidy flags
331
    // that it shoudln't be an rvalue.  rvalue keeps the API clear that the
332
    // element has been effectively moved out of even if std::move isn't
333
    // used.
334
    // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
335
    bool push(OpenSSLGeneralName&& name) const
336
0
    {
337
0
        return sk_GENERAL_NAME_push(ptr, name.release()) > 0;
338
0
    }
339
340
    OpenSSLGeneralNames(const OpenSSLGeneralNames&) = delete;
341
0
    OpenSSLGeneralNames(OpenSSLGeneralNames&& other) noexcept : ptr(other.ptr)
342
0
    {
343
0
        other.ptr = nullptr;
344
0
    }
345
    OpenSSLGeneralNames& operator=(const OpenSSLGeneralNames&) = delete;
346
    OpenSSLGeneralNames& operator=(OpenSSLGeneralNames&& other) noexcept
347
0
    {
348
0
        if (this == &other)
349
0
        {
350
0
            return *this;
351
0
        }
352
0
        GENERAL_NAMES_free(ptr);
353
0
        ptr = other.ptr;
354
0
        other.ptr = nullptr;
355
0
        return *this;
356
0
    }
357
358
    class Iterator
359
    {
360
      public:
361
        GENERAL_NAMES* names;
362
        int index;
363
364
        GENERAL_NAME& operator*() const
365
0
        {
366
0
            return *sk_GENERAL_NAME_value(names, index);
367
0
        }
368
369
        auto operator<=>(const Iterator& other) const
370
0
        {
371
0
            return index <=> other.index;
372
0
        }
373
374
        Iterator& operator++()
375
0
        {
376
0
            index++;
377
0
            return *this;
378
0
        }
379
380
        bool operator!=(const Iterator& other) const
381
0
        {
382
0
            return index != other.index;
383
0
        }
384
    };
385
386
    Iterator begin()
387
0
    {
388
0
        return Iterator(ptr, 0);
389
0
    }
390
391
    Iterator end()
392
0
    {
393
0
        return Iterator(ptr, sk_GENERAL_NAME_num(ptr));
394
0
    }
395
396
    ~OpenSSLGeneralNames()
397
0
    {
398
0
        GENERAL_NAMES_free(ptr);
399
0
    }
400
401
  private:
402
    GENERAL_NAMES* get() const
403
0
    {
404
0
        return ptr;
405
0
    }
406
407
    GENERAL_NAMES* ptr;
408
};
409
410
class OpenSSLEVPKeyCTX
411
{
412
  private:
413
0
    OpenSSLEVPKeyCTX(EVP_PKEY_CTX* ptrIn) : ptr(ptrIn) {}
414
415
  public:
416
    OpenSSLEVPKeyCTX(const OpenSSLEVPKeyCTX&) = delete;
417
0
    OpenSSLEVPKeyCTX(OpenSSLEVPKeyCTX&& other) noexcept : ptr(other.ptr)
418
0
    {
419
0
        other.ptr = nullptr;
420
0
    }
421
    OpenSSLEVPKeyCTX& operator=(const OpenSSLEVPKeyCTX&) = delete;
422
    OpenSSLEVPKeyCTX& operator=(OpenSSLEVPKeyCTX&& other) noexcept
423
0
    {
424
0
        if (this == &other)
425
0
        {
426
0
            return *this;
427
0
        }
428
0
        EVP_PKEY_CTX_free(ptr);
429
0
        ptr = other.ptr;
430
0
        other.ptr = nullptr;
431
0
        return *this;
432
0
    }
433
434
    static std::optional<OpenSSLEVPKeyCTX> newId(int id)
435
0
    {
436
0
        EVP_PKEY_CTX* ptr = EVP_PKEY_CTX_new_id(id, nullptr);
437
0
        if (ptr == nullptr)
438
0
        {
439
0
            return std::nullopt;
440
0
        }
441
0
        return {OpenSSLEVPKeyCTX(ptr)};
442
0
    }
443
444
    static std::optional<OpenSSLEVPKeyCTX> newFromPkey(OpenSSLEVPKey& pkey)
445
0
    {
446
0
        EVP_PKEY_CTX* ptr =
447
0
            EVP_PKEY_CTX_new_from_pkey(nullptr, pkey.get(), nullptr);
448
0
        if (ptr == nullptr)
449
0
        {
450
0
            return std::nullopt;
451
0
        }
452
0
        return {OpenSSLEVPKeyCTX(ptr)};
453
0
    }
454
455
    static std::optional<OpenSSLEVPKey> createPKey()
456
0
    {
457
0
        std::optional<OpenSSLEVPKeyCTX> ctx = newId(EVP_PKEY_EC);
458
0
        if (!ctx)
459
0
        {
460
0
            BMCWEB_LOG_ERROR("Failed new id");
461
0
            return std::nullopt;
462
0
        }
463
464
0
        if (!ctx->paramgenInit())
465
0
        {
466
0
            BMCWEB_LOG_ERROR("Failed to initialize paramgen");
467
0
            return std::nullopt;
468
0
        }
469
470
        // Set up curve parameters.
471
0
        if (!ctx->setEcParamEnc(OPENSSL_EC_NAMED_CURVE))
472
0
        {
473
0
            BMCWEB_LOG_ERROR("Failed to set EC parameter encoding");
474
0
            return std::nullopt;
475
0
        }
476
477
0
        if (!ctx->setEcParamGenCurveNid(NID_secp384r1))
478
0
        {
479
0
            BMCWEB_LOG_ERROR("Failed to set EC parameter generation curve NID");
480
0
            return std::nullopt;
481
0
        }
482
483
0
        std::optional<OpenSSLEVPKey> pkey = ctx->paramgen();
484
0
        if (!pkey)
485
0
        {
486
0
            BMCWEB_LOG_ERROR("Failed to generate EC key");
487
0
            return std::nullopt;
488
0
        }
489
490
        // Set new context for key generation, using curve parameters.
491
        // Don't check here; pkey only has parameters, not a full key yet.
492
0
        ctx = OpenSSLEVPKeyCTX::newFromPkey(*pkey);
493
0
        if (!ctx)
494
0
        {
495
0
            BMCWEB_LOG_ERROR("Failed to create new context for key generation");
496
0
            return std::nullopt;
497
0
        }
498
499
0
        if (!ctx->keygenInit())
500
0
        {
501
0
            BMCWEB_LOG_ERROR("Failed to initialize keygen");
502
0
            return std::nullopt;
503
0
        }
504
505
        // Generate key.
506
0
        if (!ctx->keygen(*pkey))
507
0
        {
508
0
            BMCWEB_LOG_ERROR("Failed to generate key");
509
0
            return std::nullopt;
510
0
        }
511
512
0
        if (!ctx->check())
513
0
        {
514
0
            BMCWEB_LOG_ERROR("Generated EC key failed validation");
515
0
            return std::nullopt;
516
0
        }
517
518
0
        return pkey;
519
0
    }
520
521
    bool keygenInit()
522
0
    {
523
0
        return EVP_PKEY_keygen_init(ptr) == 1;
524
0
    }
525
526
    bool paramgenInit()
527
0
    {
528
0
        return EVP_PKEY_paramgen_init(ptr) == 1;
529
0
    }
530
531
    bool check()
532
0
    {
533
0
        int r = EVP_PKEY_check(ptr);
534
0
        if (r != 1)
535
0
        {
536
0
            BMCWEB_LOG_ERROR("Failed to check private key {}", r);
537
0
        }
538
0
        return r == 1;
539
0
    }
540
541
    bool keygen(OpenSSLEVPKey& pkey) const
542
0
    {
543
0
        return EVP_PKEY_keygen(get(), &pkey.ptr) == 1;
544
0
    }
545
546
    bool setEcParamEnc(int enc) const
547
0
    {
548
0
        return EVP_PKEY_CTX_set_ec_param_enc(get(), enc) == 1;
549
0
    }
550
551
    bool setEcParamGenCurveNid(int nid) const
552
0
    {
553
0
        return EVP_PKEY_CTX_set_ec_paramgen_curve_nid(get(), nid) == 1;
554
0
    }
555
556
    std::optional<OpenSSLEVPKey> paramgen() const
557
0
    {
558
0
        OpenSSLEVPKey ret(nullptr);
559
0
        if (EVP_PKEY_paramgen(get(), &ret.ptr) != 1)
560
0
        {
561
0
            return std::nullopt;
562
0
        }
563
0
        return {std::move(ret)};
564
0
    }
565
566
    ~OpenSSLEVPKeyCTX()
567
0
    {
568
0
        EVP_PKEY_CTX_free(ptr);
569
0
    }
570
571
    EVP_PKEY_CTX* get() const
572
0
    {
573
0
        return ptr;
574
0
    }
575
576
  private:
577
    EVP_PKEY_CTX* ptr;
578
};
579
580
class OpenSSLX509StoreCTX;
581
class OpenSSLSSL;
582
class OpenSSLX509;
583
584
class OpenSSLX509Name
585
{
586
  public:
587
    friend OpenSSLX509;
588
589
    explicit OpenSSLX509Name(const X509_NAME* nameIn) :
590
0
        ptr(X509_NAME_dup(nameIn))
591
0
    {}
592
593
    OpenSSLX509Name(const OpenSSLX509Name&) = delete;
594
    OpenSSLX509Name(OpenSSLX509Name&&) noexcept = delete;
595
    OpenSSLX509Name& operator=(const OpenSSLX509Name&) = delete;
596
    OpenSSLX509Name& operator=(OpenSSLX509Name&&) noexcept = delete;
597
598
    bool addEntryByTxt(std::string_view field, std::string_view value) const
599
0
    {
600
0
        if (ptr == nullptr)
601
0
        {
602
0
            return false;
603
0
        }
604
0
        std::string fieldStr(field);
605
0
        const unsigned char* valuePtr =
606
0
            std::bit_cast<const unsigned char*>(value.data());
607
0
        int valueLength = static_cast<int>(value.size());
608
0
        return X509_NAME_add_entry_by_txt(ptr, fieldStr.c_str(), MBSTRING_ASC,
609
0
                                          valuePtr, valueLength, -1, 0) == 1;
610
0
    }
611
612
    ~OpenSSLX509Name()
613
0
    {
614
0
        X509_NAME_free(ptr);
615
0
    }
616
617
  private:
618
    X509_NAME* get() const
619
0
    {
620
0
        return ptr;
621
0
    }
622
623
    X509_NAME* ptr;
624
};
625
626
class OpenSSLX509
627
{
628
    friend OpenSSLX509StoreCTX;
629
    friend OpenSSLEVPKeyCTX;
630
    friend OpenSSLSSLCtx;
631
    friend OpenSSLSSL;
632
633
  private:
634
0
    OpenSSLX509(X509* ptrIn) : ptr(ptrIn), ptrOwned(false) {}
635
636
  public:
637
0
    OpenSSLX509() : ptr(X509_new()) {}
638
    OpenSSLX509(const OpenSSLX509&) = delete;
639
0
    OpenSSLX509(OpenSSLX509&& other) noexcept : ptr(other.ptr)
640
0
    {
641
0
        other.ptr = nullptr;
642
0
    }
643
    OpenSSLX509& operator=(const OpenSSLX509&) = delete;
644
    OpenSSLX509& operator=(OpenSSLX509&& other) noexcept
645
0
    {
646
0
        if (this == &other)
647
0
        {
648
0
            return *this;
649
0
        }
650
0
        X509_free(ptr);
651
0
        ptr = other.ptr;
652
0
        other.ptr = nullptr;
653
0
        return *this;
654
0
    }
655
656
    static std::optional<OpenSSLX509> loadFromPEMData(std::string& data)
657
0
    {
658
0
        OpenSSLBIO bufio(data);
659
0
        X509* ptr = PEM_read_bio_X509(bufio.get(), nullptr, nullptr, nullptr);
660
0
        if (ptr == nullptr)
661
0
        {
662
0
            return std::nullopt;
663
0
        }
664
0
        return {OpenSSLX509(ptr)};
665
0
    }
666
667
    bool pemWriteBioX509(OpenSSLBIO& bufio) const
668
0
    {
669
0
        return PEM_write_bio_X509(bufio.get(), ptr) > 0;
670
0
    }
671
672
    bool addSubjectEntry(std::string_view field, std::string_view value) const
673
0
    {
674
0
        OpenSSLX509Name name(X509_get_subject_name(ptr));
675
0
        return name.addEntryByTxt(field, value) &&
676
0
               X509_set_subject_name(ptr, name.get()) == 1;
677
0
    }
678
679
    bool setSubjectName(std::string_view cn) const
680
0
    {
681
0
        return addSubjectEntry("CN", cn);
682
0
    }
683
684
    bool setCountry(std::string_view country) const
685
0
    {
686
0
        return addSubjectEntry("C", country);
687
0
    }
688
689
    bool setOrganization(std::string_view organization) const
690
0
    {
691
0
        return addSubjectEntry("O", organization);
692
0
    }
693
694
    bool setIssuerNameToSubject() const
695
0
    {
696
0
        return X509_set_issuer_name(ptr, X509_get_subject_name(ptr)) == 1;
697
0
    }
698
699
    void setSerialNumber(uint64_t n) const
700
0
    {
701
0
        ASN1_INTEGER_set_uint64(X509_get_serialNumber(ptr), n);
702
0
    }
703
704
    void setValidityPeriodFromNow(
705
        std::chrono::system_clock::duration duration) const
706
0
    {
707
        // not before this moment
708
0
        X509_gmtime_adj(X509_getm_notBefore(ptr), 0);
709
0
        using std::chrono::floor;
710
0
        using OsslSeconds = std::chrono::duration<long>;
711
0
        OsslSeconds durationSeconds = floor<OsslSeconds>(duration);
712
0
        X509_gmtime_adj(X509_getm_notAfter(ptr), durationSeconds.count());
713
0
    }
714
715
    bool setPubkey(OpenSSLEVPKey& pkey) const
716
0
    {
717
0
        return X509_set_pubkey(ptr, pkey.get()) == 1;
718
0
    }
719
720
    bool setVersion(int version) const
721
0
    {
722
0
        return X509_set_version(ptr, version) == 1;
723
0
    }
724
725
    bool addAltNameUpns(std::initializer_list<std::string_view> upns) const
726
0
    {
727
0
        return addAltNames(NID_ms_upn, upns);
728
0
    }
729
730
    bool addAltNameEmails(
731
        std::initializer_list<std::string_view> altNames) const
732
0
    {
733
0
        return addAltNames(GEN_EMAIL, altNames);
734
0
    }
735
736
    bool addAltNames(int gnType,
737
                     std::initializer_list<std::string_view> altNames) const
738
0
    {
739
0
        OpenSSLGeneralNames gens;
740
0
        for (const std::string_view altName : altNames)
741
0
        {
742
0
            OpenSSLGeneralName gen(gnType, altName);
743
0
            if (!gens.push(std::move(gen)))
744
0
            {
745
0
                return false;
746
0
            }
747
0
        }
748
0
749
0
        return add1ExtI2d(NID_subject_alt_name, gens) > 0;
750
0
    }
751
752
    int add1ExtI2d(int nid, OpenSSLGeneralNames& names) const
753
0
    {
754
0
        return X509_add1_ext_i2d(ptr, nid, names.get(), 0, 0);
755
0
    }
756
757
    bool sign() const
758
0
    {
759
0
        // Generate test key
760
0
        std::optional<OpenSSLEVPKeyCTX> ctx =
761
0
            OpenSSLEVPKeyCTX::newId(EVP_PKEY_EC);
762
0
        if (!ctx)
763
0
        {
764
0
            return false;
765
0
        }
766
0
        if (!ctx->keygenInit())
767
0
        {
768
0
            return false;
769
0
        }
770
0
        if (!ctx->setEcParamGenCurveNid(NID_X9_62_prime256v1))
771
0
        {
772
0
            return false;
773
0
        }
774
0
775
0
        OpenSSLEVPKey key = OpenSSLEVPKey::fromKeygen(ctx->get());
776
0
777
0
        // Sign cert with key
778
0
        if (X509_set_pubkey(ptr, key.ptr) != 1)
779
0
        {
780
0
            return false;
781
0
        }
782
0
        if (X509_sign(ptr, key.ptr, EVP_sha256()) <= 0)
783
0
        {
784
0
            return false;
785
0
        }
786
0
        return true;
787
0
    }
788
789
    bool sign(OpenSSLEVPKey& pkey) const
790
0
    {
791
0
        return X509_sign(ptr, pkey.get(), EVP_sha256()) > 0;
792
0
    }
793
794
    std::optional<OpenSSLEVPKey> getPubKey() const
795
0
    {
796
0
        EVP_PKEY* pkey = X509_get_pubkey(ptr);
797
0
        if (pkey == nullptr)
798
0
        {
799
0
            return std::nullopt;
800
0
        }
801
0
        return {OpenSSLEVPKey(pkey)};
802
0
    }
803
804
    std::string getCommonName() const
805
0
    {
806
        // Extract username contained in CommonName
807
0
        const X509_NAME* name = X509_get_subject_name(ptr);
808
0
        int index = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
809
0
        if (index < 0)
810
0
        {
811
0
            BMCWEB_LOG_DEBUG("TLS cannot get common name to create session");
812
0
            return "";
813
0
        }
814
0
        const X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, index);
815
0
        const ASN1_STRING* data = X509_NAME_ENTRY_get_data(entry);
816
0
        if (data == nullptr)
817
0
        {
818
0
            BMCWEB_LOG_DEBUG("TLS cannot get common name to create session");
819
0
            return "";
820
0
        }
821
0
        int length = ASN1_STRING_length(data);
822
0
        if (length <= 0)
823
0
        {
824
0
            BMCWEB_LOG_DEBUG("TLS common name is empty");
825
0
            return "";
826
0
        }
827
0
        return {std::bit_cast<const char*>(ASN1_STRING_get0_data(data)),
828
0
                static_cast<size_t>(length)};
829
0
    }
830
831
    std::string getComment() const
832
0
    {
833
0
        ASN1_STRING* r = static_cast<ASN1_STRING*>(
834
0
            X509_get_ext_d2i(ptr, NID_netscape_comment, nullptr, nullptr));
835
0
        if (r == nullptr)
836
0
        {
837
0
            return "";
838
0
        }
839
0
        OpenSSLASN1String asn1(static_cast<ASN1_IA5STRING*>(r));
840
0
        return std::string(asn1.getAsString());
841
0
    }
842
843
    int verify(OpenSSLEVPKey& key) const
844
0
    {
845
0
        return X509_verify(ptr, key.ptr);
846
0
    }
847
848
    int addExt(int nid, std::string_view value) const
849
0
    {
850
0
        X509V3_CTX ctx{};
851
0
        X509V3_set_ctx(&ctx, ptr, ptr, nullptr, nullptr, 0);
852
0
        OpenSSLX509Extension ex(&ctx, nid, value);
853
0
        X509_add_ext(ptr, ex.get(), -1);
854
0
        return 0;
855
0
    }
856
857
    std::optional<OpenSSLASN1String> getExtStr(int nid) const
858
0
    {
859
0
        ASN1_STRING* out = static_cast<ASN1_STRING*>(
860
0
            X509_get_ext_d2i(ptr, nid, nullptr, nullptr));
861
0
        if (out == nullptr)
862
0
        {
863
0
            return std::nullopt;
864
0
        }
865
0
        return {OpenSSLASN1String(out)};
866
0
    }
867
868
    std::optional<OpenSSLGeneralNames> getAltNames() const
869
0
    {
870
0
        GENERAL_NAMES* ext = static_cast<GENERAL_NAMES*>(
871
0
            X509_get_ext_d2i(ptr, NID_subject_alt_name, nullptr, nullptr));
872
0
        if (ext == nullptr)
873
0
        {
874
0
            return std::nullopt;
875
0
        }
876
0
        return {OpenSSLGeneralNames(ext)};
877
0
    }
878
879
    static std::optional<OpenSSLX509> fromPEMData(std::string& data)
880
0
    {
881
0
        OpenSSLBIO bufio(data);
882
0
        return fromBioFile(bufio);
883
0
    }
884
885
    static std::optional<OpenSSLX509> fromPEMFile(
886
        const std::filesystem::path& filepath)
887
0
    {
888
0
        OpenSSLBIO bufio(filepath);
889
0
        return fromBioFile(bufio);
890
0
    }
891
892
    bool checkPurpose(int purpose) const
893
0
    {
894
0
        return X509_check_purpose(ptr, purpose, 0) == 1;
895
0
    }
896
897
    static std::optional<OpenSSLX509> fromBioFile(OpenSSLBIO& bufio)
898
0
    {
899
0
        X509* ptr = PEM_read_bio_X509(bufio.get(), nullptr, nullptr, nullptr);
900
0
        if (ptr == nullptr)
901
0
        {
902
0
            return std::nullopt;
903
0
        }
904
0
        return {OpenSSLX509(ptr)};
905
0
    }
906
907
    ~OpenSSLX509()
908
0
    {
909
0
        if (ptrOwned)
910
0
        {
911
0
            X509_free(ptr);
912
0
        }
913
0
    }
914
915
  private:
916
    X509* get() const
917
0
    {
918
0
        return ptr;
919
0
    }
920
921
    X509* ptr;
922
    bool ptrOwned = true;
923
};
924
925
class OpenSSLX509Store
926
{
927
  public:
928
    friend OpenSSLX509StoreCTX;
929
    friend OpenSSLSSL;
930
    friend OpenSSLSSLCtx;
931
932
0
    OpenSSLX509Store() : ptr(X509_STORE_new()) {}
933
934
    OpenSSLX509Store(const OpenSSLX509Store&) = delete;
935
    OpenSSLX509Store(OpenSSLX509Store&& other) noexcept : ptr(other.ptr)
936
0
    {
937
0
        other.ptr = nullptr;
938
0
    }
939
    OpenSSLX509Store& operator=(const OpenSSLX509Store&) = delete;
940
    OpenSSLX509Store& operator=(OpenSSLX509Store&& other) noexcept
941
0
    {
942
0
        if (this == &other)
943
0
        {
944
0
            return *this;
945
0
        }
946
0
        X509_STORE_free(ptr);
947
0
        ptr = other.ptr;
948
0
        other.ptr = nullptr;
949
0
        return *this;
950
0
    }
951
    ~OpenSSLX509Store()
952
0
    {
953
0
        X509_STORE_free(ptr);
954
0
    }
955
956
  private:
957
    X509_STORE* get() const
958
0
    {
959
0
        return ptr;
960
0
    }
961
    X509_STORE* ptr;
962
};
963
964
class OpenSSLX509StoreCTX
965
{
966
  public:
967
0
    OpenSSLX509StoreCTX() : ptr(X509_STORE_CTX_new()) {}
968
969
    OpenSSLX509StoreCTX(const OpenSSLX509StoreCTX&) = delete;
970
    OpenSSLX509StoreCTX(OpenSSLX509StoreCTX&& other) noexcept : ptr(other.ptr)
971
0
    {
972
0
        other.ptr = nullptr;
973
0
    }
974
    OpenSSLX509StoreCTX& operator=(const OpenSSLX509StoreCTX&) = delete;
975
    OpenSSLX509StoreCTX& operator=(OpenSSLX509StoreCTX&& other) noexcept
976
0
    {
977
0
        if (this == &other)
978
0
        {
979
0
            return *this;
980
0
        }
981
0
        X509_STORE_CTX_free(ptr);
982
0
        ptr = other.ptr;
983
0
        other.ptr = nullptr;
984
0
        return *this;
985
0
    }
986
987
    int init(OpenSSLX509Store& store, OpenSSLX509& cert)
988
0
    {
989
0
        return X509_STORE_CTX_init(ptr, store.get(), cert.get(), nullptr);
990
0
    }
991
992
    int verifyCert()
993
0
    {
994
0
        return X509_verify_cert(ptr);
995
0
    }
996
997
    ~OpenSSLX509StoreCTX()
998
0
    {
999
0
        X509_STORE_CTX_free(ptr);
1000
0
    }
1001
1002
    static std::optional<OpenSSLX509StoreCTX> fromCert(OpenSSLX509& cert)
1003
0
    {
1004
0
        OpenSSLX509StoreCTX os;
1005
0
        X509_STORE_CTX_set_current_cert(os.get(), cert.get());
1006
0
        return {std::move(os)};
1007
0
    }
1008
1009
    int getError() const
1010
0
    {
1011
0
        return X509_STORE_CTX_get_error(ptr);
1012
0
    }
1013
1014
    boost::asio::ssl::verify_context releaseToVerifyContext()
1015
0
    {
1016
0
        X509_STORE_CTX* temp = ptr;
1017
0
        ptr = nullptr;
1018
0
        return boost::asio::ssl::verify_context(temp);
1019
0
    }
1020
1021
  private:
1022
    X509_STORE_CTX* get() const
1023
0
    {
1024
0
        return ptr;
1025
0
    }
1026
1027
    X509_STORE_CTX* ptr;
1028
};
1029
1030
class OpenSSLSSLCtx
1031
{
1032
  public:
1033
    friend OpenSSLSSL;
1034
1035
    static OpenSSLSSLCtx createServerCtx()
1036
0
    {
1037
0
        OpenSSLSSLCtx ctx(SSL_CTX_new(TLS_server_method()));
1038
0
        ctx.ptrOwned = true;
1039
0
        return ctx;
1040
0
    }
1041
    static OpenSSLSSLCtx createClientCtx()
1042
0
    {
1043
0
        OpenSSLSSLCtx ctx(SSL_CTX_new(TLS_client_method()));
1044
0
        ctx.ptrOwned = true;
1045
0
        return ctx;
1046
0
    }
1047
1048
    OpenSSLSSLCtx(const OpenSSLSSLCtx&) = delete;
1049
    OpenSSLSSLCtx& operator=(const OpenSSLSSLCtx&) = delete;
1050
    OpenSSLSSLCtx(OpenSSLSSLCtx&& other) noexcept :
1051
        ptr(other.ptr), ptrOwned(other.ptrOwned)
1052
0
    {
1053
0
        other.ptr = nullptr;
1054
0
        other.ptrOwned = false;
1055
0
    }
1056
1057
    OpenSSLSSLCtx& operator=(OpenSSLSSLCtx&& other) noexcept
1058
0
    {
1059
0
        if (this == &other)
1060
0
        {
1061
0
            return *this;
1062
0
        }
1063
0
        SSL_CTX_free(ptr);
1064
0
        ptr = other.ptr;
1065
0
        other.ptr = nullptr;
1066
0
        ptrOwned = other.ptrOwned;
1067
0
        return *this;
1068
0
    }
1069
    ~OpenSSLSSLCtx()
1070
0
    {
1071
0
        if (ptrOwned)
1072
0
        {
1073
0
            SSL_CTX_free(ptr);
1074
0
        }
1075
0
    }
1076
1077
    bool valid() const
1078
0
    {
1079
0
        return ptr != nullptr;
1080
0
    }
1081
1082
    bool useCertificate(const OpenSSLX509& cert) const
1083
0
    {
1084
0
        return SSL_CTX_use_certificate(ptr, cert.get()) == 1;
1085
0
    }
1086
1087
    bool usePrivateKey(const OpenSSLEVPKey& pkey) const
1088
0
    {
1089
0
        return SSL_CTX_use_PrivateKey(ptr, pkey.get()) == 1;
1090
0
    }
1091
1092
    void setVerifyPeer() const
1093
0
    {
1094
0
        SSL_CTX_set_verify(ptr, SSL_VERIFY_PEER, nullptr);
1095
0
    }
1096
1097
    bool addCertToStore(const OpenSSLX509& cert) const
1098
0
    {
1099
0
        X509_STORE* store = SSL_CTX_get_cert_store(ptr);
1100
0
        if (store == nullptr)
1101
0
        {
1102
0
            return false;
1103
0
        }
1104
0
        return X509_STORE_add_cert(store, cert.get()) == 1;
1105
0
    }
1106
1107
    bool setCiphersuites(std::string_view ciphersuites)
1108
0
    {
1109
0
        std::string ciphersuitesStr(ciphersuites);
1110
0
        if (SSL_CTX_set_ciphersuites(ptr, ciphersuitesStr.data()) != 1)
1111
0
        {
1112
0
            BMCWEB_LOG_ERROR("Error setting TLS ciphersuites");
1113
0
            return false;
1114
0
        }
1115
0
        return true;
1116
0
    }
1117
1118
    bool setCurves(std::string_view curves)
1119
0
    {
1120
0
        std::string curvesStr(curves);
1121
0
        if (SSL_CTX_set1_curves_list(ptr, curvesStr.data()) != 1)
1122
0
        {
1123
0
            BMCWEB_LOG_ERROR("Error setting TLS curve list");
1124
0
            return false;
1125
0
        }
1126
0
        return true;
1127
0
    }
1128
1129
    bool setCipherList(std::string_view cipherList)
1130
0
    {
1131
0
        std::string cipherListStr(cipherList);
1132
0
        if (SSL_CTX_set_cipher_list(ptr, cipherListStr.data()) != 1)
1133
0
        {
1134
0
            BMCWEB_LOG_ERROR("Error setting cipher list");
1135
0
            return false;
1136
0
        }
1137
0
        return true;
1138
0
    }
1139
1140
0
    explicit OpenSSLSSLCtx(SSL_CTX* ptrIn) : ptr(ptrIn), ptrOwned(false) {}
1141
1142
  private:
1143
    SSL_CTX* get() const
1144
0
    {
1145
0
        return ptr;
1146
0
    }
1147
1148
    SSL_CTX* ptr;
1149
    bool ptrOwned = true;
1150
};
1151
1152
class OpenSSLSSL
1153
{
1154
  public:
1155
0
    explicit OpenSSLSSL(const OpenSSLSSLCtx& ctx) : ptr(SSL_new(ctx.get())) {}
1156
0
    explicit OpenSSLSSL(SSL* ptrIn) : ptr(ptrIn), ptrOwned(false) {}
1157
1158
    OpenSSLSSL(const OpenSSLSSL&) = delete;
1159
    OpenSSLSSL& operator=(const OpenSSLSSL&) = delete;
1160
    OpenSSLSSL(OpenSSLSSL&& other) noexcept : ptr(other.ptr)
1161
0
    {
1162
0
        other.ptr = nullptr;
1163
0
    }
1164
    OpenSSLSSL& operator=(OpenSSLSSL&& other) noexcept
1165
0
    {
1166
0
        if (this == &other)
1167
0
        {
1168
0
            return *this;
1169
0
        }
1170
0
        SSL_free(ptr);
1171
0
        ptr = other.ptr;
1172
0
        other.ptr = nullptr;
1173
0
        return *this;
1174
0
    }
1175
    ~OpenSSLSSL()
1176
0
    {
1177
0
        if (ptrOwned)
1178
0
        {
1179
0
            SSL_free(ptr);
1180
0
        }
1181
0
    }
1182
1183
    bool valid() const
1184
0
    {
1185
0
        return ptr != nullptr;
1186
0
    }
1187
1188
    // Creates two memory BIOs for read/write and hands them to the SSL.
1189
    // SSL_set_bio takes ownership, so the SSL_free in the destructor frees
1190
    // them.
1191
    bool setMemBio() const
1192
0
    {
1193
0
        BIO* rbio = BIO_new(BIO_s_mem());
1194
0
        BIO* wbio = BIO_new(BIO_s_mem());
1195
0
        if (rbio == nullptr || wbio == nullptr)
1196
0
        {
1197
0
            BIO_free(rbio);
1198
0
            BIO_free(wbio);
1199
0
            return false;
1200
0
        }
1201
0
        SSL_set_bio(ptr, rbio, wbio);
1202
0
        return true;
1203
0
    }
1204
1205
    void setAcceptState() const
1206
0
    {
1207
0
        SSL_set_accept_state(ptr);
1208
0
    }
1209
1210
    void setConnectState() const
1211
0
    {
1212
0
        SSL_set_connect_state(ptr);
1213
0
    }
1214
1215
    bool useCertificate(const OpenSSLX509& cert) const
1216
0
    {
1217
0
        return SSL_use_certificate(ptr, cert.get()) == 1;
1218
0
    }
1219
1220
    bool usePrivateKey(const OpenSSLEVPKey& pkey) const
1221
0
    {
1222
0
        return SSL_use_PrivateKey(ptr, pkey.get()) == 1;
1223
0
    }
1224
1225
    int doHandshake() const
1226
0
    {
1227
0
        return SSL_do_handshake(ptr);
1228
0
    }
1229
1230
    std::optional<OpenSSLX509> getPeerCertificate() const
1231
0
    {
1232
0
        X509* cert = SSL_get1_peer_certificate(ptr);
1233
0
        if (cert == nullptr)
1234
0
        {
1235
0
            return std::nullopt;
1236
0
        }
1237
0
        return {OpenSSLX509(cert)};
1238
0
    }
1239
1240
    void transferTo(OpenSSLSSL& other) const
1241
0
    {
1242
0
        BIO* fromWbio = SSL_get_wbio(ptr);
1243
0
        BIO* toRbio = SSL_get_rbio(other.ptr);
1244
0
        std::array<char, 4096> buf{};
1245
0
        int pending = static_cast<int>(BIO_ctrl_pending(fromWbio));
1246
0
        while (pending > 0)
1247
0
        {
1248
0
            int n = BIO_read(fromWbio, buf.data(),
1249
0
                             std::min(pending, static_cast<int>(buf.size())));
1250
0
            if (n > 0)
1251
0
            {
1252
0
                BIO_write(toRbio, buf.data(), n);
1253
0
            }
1254
0
            pending = static_cast<int>(BIO_ctrl_pending(fromWbio));
1255
0
        }
1256
0
    }
1257
1258
    long getVerifyResult() const
1259
0
    {
1260
0
        return SSL_get_verify_result(ptr);
1261
0
    }
1262
1263
  private:
1264
    SSL* get() const
1265
0
    {
1266
0
        return ptr;
1267
0
    }
1268
1269
    SSL* ptr;
1270
    bool ptrOwned = true;
1271
};