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 }