winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

Certificates.h (8535B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #pragma once
      4 #include <wincrypt.h>
      5 
      6 #include <json/forwards.h>
      7 #include <wil/resource.h>
      8 
      9 #include <functional>
     10 #include <ostream>
     11 #include <vector>
     12 
     13 
     14 namespace AppInstaller::Certificates
     15 {
     16     // Defines the types of certificate pinning to perform.
     17     enum class PinningVerificationType : uint32_t
     18     {
     19         // No validation; accepts anything.
     20         None = 0x0,
     21         // Validates that the public keys match; requires a certificate to be loaded.
     22         PublicKey = 0x1,
     23         // Validates that the full subjects match; requires a certificate to be loaded.
     24         Subject = 0x2,
     25         // Validates that the full issuers match; requires a certificate to be loaded.
     26         Issuer = 0x4,
     27         // Allows for unknown certificates in the chain; will continue to consume certificates from the chain until it matches.
     28         // Requires partial chain.
     29         AnyIssuer = 0x8,
     30         // Requires that the certificate is not a leaf.
     31         RequireNonLeaf = 0x10,
     32     };
     33 
     34     DEFINE_ENUM_FLAG_OPERATORS(PinningVerificationType);
     35 
     36     std::ostream& operator<<(std::ostream& out, PinningVerificationType value);
     37 
     38     // The position within a chain that a certificate is located.
     39     enum class CertificateChainPosition
     40     {
     41         Unknown = 0x0,
     42         // The start of the chain.
     43         Root = 0x1,
     44         // An indeterminate intermediate along the chain.
     45         Intermediate = 0x2,
     46         // The final certificate of the chain.
     47         Leaf = 0x4,
     48     };
     49 
     50     DEFINE_ENUM_FLAG_OPERATORS(CertificateChainPosition);
     51 
     52     std::ostream& operator<<(std::ostream& out, CertificateChainPosition value);
     53 
     54     // The result of validating a single certificate.
     55     enum class CertificatePinningValidationResult
     56     {
     57         // The certificate was accepted as valid.
     58         Accepted,
     59         // The certificate was rejected as invalid.
     60         Rejected,
     61         // The next certificate in the chain should be validated against the current details.
     62         // For use by partial chain validation that does not require exacting chain configurations.
     63         Skipped,
     64     };
     65 
     66     // Contains the specific information about a certificate to pin.
     67     struct PinningDetails
     68     {
     69         PinningDetails() = default;
     70 
     71         PinningDetails(const PinningDetails&) = default;
     72         PinningDetails& operator=(const PinningDetails&) = default;
     73 
     74         PinningDetails(PinningDetails&&) = default;
     75         PinningDetails& operator=(PinningDetails&&) = default;
     76 
     77         // Loads the certificate context.
     78         PinningDetails& LoadCertificate(int resource, int resourceType);
     79         PinningDetails& LoadCertificate(const std::vector<BYTE>& certificateBytes);
     80         PinningDetails& LoadCertificate(const std::pair<const BYTE*,size_t> certificateBytes);
     81         PCCERT_CONTEXT GetCertificate() const { return m_certificateContext.get(); }
     82 
     83         PinningDetails& SetPinning(PinningVerificationType type);
     84         PinningVerificationType GetPinning() const { return m_pinning; }
     85 
     86         // Validates the given certificate against the pinning information.
     87         CertificatePinningValidationResult Validate(PCCERT_CONTEXT certContext, CertificateChainPosition position) const;
     88 
     89         // Outputs a description of the pinning details.
     90         void OutputDescription(std::ostream& stream, std::string_view indent) const;
     91 
     92         // Loads the pinning details from the given JSON.
     93         [[nodiscard]] bool LoadFrom(const Json::Value& configuration);
     94 
     95         // Determines how far the certificate is through its lifespan.
     96         double GetRemainingLifetimePercentage() const;
     97 
     98 #ifndef AICLI_DISABLE_TEST_HOOKS
     99         using CustomValidationFunction = std::function<bool(const PinningDetails&, PCCERT_CONTEXT, CertificateChainPosition)>;
    100         void SetCustomValidationFunction(CustomValidationFunction function) { m_customValidation = std::move(function); }
    101     private:
    102         CustomValidationFunction m_customValidation;
    103 #endif
    104 
    105     private:
    106         wil::shared_cert_context m_certificateContext;
    107         PinningVerificationType m_pinning = PinningVerificationType::None;
    108     };
    109 
    110     // Contains the full chain of pinning details.
    111     struct PinningChain
    112     {
    113         PinningChain() = default;
    114 
    115         PinningChain(const PinningChain&) = default;
    116         PinningChain& operator=(const PinningChain&) = default;
    117 
    118         PinningChain(PinningChain&&) = default;
    119         PinningChain& operator=(PinningChain&&) = default;
    120 
    121         // A single entry in the chain.
    122         struct Node
    123         {
    124             friend PinningChain;
    125 
    126             // Access the value
    127             PinningDetails* operator->() { return &m_chain.get()[m_index]; }
    128 
    129             // Create/access the next node in the chain.
    130             Node Next();
    131             const Node Next() const;
    132 
    133             // Drops the next node (and all subsequent nodes) from the chain.
    134             void RemoveNext();
    135 
    136             // Indicates if there is already an existing next node.
    137             bool HasNext() const;
    138 
    139         private:
    140             Node(std::vector<PinningDetails>& chain, size_t index);
    141 
    142             std::reference_wrapper<std::vector<PinningDetails>> m_chain;
    143             size_t m_index = 0;
    144         };
    145 
    146         // Gets the root certificate pinning details in the chain.
    147         // These will correspond to the root of the certificate chain being verified.
    148         Node Root();
    149         const Node Root() const;
    150 
    151         // A partial chain will validate success if all of its components are successful.
    152         PinningChain& PartialChain(bool isPartial = true);
    153         bool IsPartialChain() const { return m_partial; }
    154 
    155         // Validates the given certificate chain against the configuration.
    156         // Returns true to indicate that the chain meets the pinning configuration criteria.
    157         // Returns false to indicate the it does not.
    158         bool Validate(PCCERT_CHAIN_CONTEXT chainContext) const;
    159 
    160         // Gets a description of the pinning chain.
    161         std::string GetDescription() const;
    162 
    163         // Loads the pinning chain from the given JSON.
    164         [[nodiscard]] bool LoadFrom(const Json::Value& configuration);
    165 
    166         // Determines how far the certificate chain is through its lifespan (the minimum of all of its certificates).
    167         double GetRemainingLifetimePercentage() const;
    168 
    169     private:
    170         std::vector<PinningDetails> m_chain;
    171         bool m_partial = false;
    172     };
    173 
    174     // Holds the details about how a certificate chain is to be validated (aka "pinned").
    175     struct PinningConfiguration
    176     {
    177         PinningConfiguration(std::string identifier = {});
    178 
    179         PinningConfiguration(const PinningConfiguration&) = default;
    180         PinningConfiguration& operator=(const PinningConfiguration&) = default;
    181 
    182         PinningConfiguration(PinningConfiguration&&) = default;
    183         PinningConfiguration& operator=(PinningConfiguration&&) = default;
    184 
    185         // Adds a possible chain to the configuration.
    186         // For a certificate to be valid, it must match only one of the configured chains.
    187         void AddChain(PinningChain chain);
    188 
    189         // Validates the given leaf certificate against the configuration.
    190         // Returns true to indicate that the certificate meets the pinning configuration criteria.
    191         // Returns false to indicate the it does not.
    192         bool Validate(PCCERT_CONTEXT certContext) const;
    193 
    194         // True if no pinning is configured.
    195         bool IsEmpty() const { return m_configuration.empty(); }
    196 
    197         // Loads the pinning configuration from the given JSON.
    198         [[nodiscard]] bool LoadFrom(const Json::Value& configuration);
    199 
    200         // Determines how far the configuration is through its lifespan (the maximum of all of its chains).
    201         double GetRemainingLifetimePercentage() const;
    202 
    203     private:
    204         // The identifier used when logging.
    205         std::string m_identifier;
    206 
    207         // The configured chains.
    208         std::vector<PinningChain> m_configuration;
    209 
    210         // We store the last certificate that was successfully validated to speed up subsequent checks.
    211         // Only cache a single certificate under the assumption that most of the time there will
    212         // only be a single server certificate in use.
    213         mutable std::vector<BYTE> m_cachedCertificate;
    214     };
    215 }