xref: /aosp_15_r20/external/openscreen/cast/common/certificate/cast_cert_validator.cc (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "cast/common/certificate/cast_cert_validator.h"
6 
7 #include <openssl/digest.h>
8 #include <openssl/x509.h>
9 #include <openssl/x509v3.h>
10 #include <stddef.h>
11 #include <stdint.h>
12 #include <string.h>
13 
14 #include <algorithm>
15 #include <memory>
16 #include <utility>
17 
18 #include "cast/common/certificate/cast_cert_validator_internal.h"
19 #include "cast/common/certificate/cast_crl.h"
20 #include "cast/common/certificate/cast_trust_store.h"
21 #include "util/osp_logging.h"
22 
23 namespace openscreen {
24 namespace cast {
25 namespace {
26 
27 // Returns the OID for the Audio-Only Cast policy
28 // (1.3.6.1.4.1.11129.2.5.2) in DER form.
AudioOnlyPolicyOid()29 const ConstDataSpan& AudioOnlyPolicyOid() {
30   static const uint8_t kAudioOnlyPolicy[] = {0x2B, 0x06, 0x01, 0x04, 0x01,
31                                              0xD6, 0x79, 0x02, 0x05, 0x02};
32   static ConstDataSpan kPolicySpan{kAudioOnlyPolicy, sizeof(kAudioOnlyPolicy)};
33   return kPolicySpan;
34 }
35 
36 class CertVerificationContextImpl final : public CertVerificationContext {
37  public:
CertVerificationContextImpl(bssl::UniquePtr<EVP_PKEY> cert,std::string common_name)38   CertVerificationContextImpl(bssl::UniquePtr<EVP_PKEY> cert,
39                               std::string common_name)
40       : public_key_{std::move(cert)}, common_name_(std::move(common_name)) {}
41 
42   ~CertVerificationContextImpl() override = default;
43 
VerifySignatureOverData(const ConstDataSpan & signature,const ConstDataSpan & data,DigestAlgorithm digest_algorithm) const44   bool VerifySignatureOverData(
45       const ConstDataSpan& signature,
46       const ConstDataSpan& data,
47       DigestAlgorithm digest_algorithm) const override {
48     const EVP_MD* digest;
49     switch (digest_algorithm) {
50       case DigestAlgorithm::kSha1:
51         digest = EVP_sha1();
52         break;
53       case DigestAlgorithm::kSha256:
54         digest = EVP_sha256();
55         break;
56       case DigestAlgorithm::kSha384:
57         digest = EVP_sha384();
58         break;
59       case DigestAlgorithm::kSha512:
60         digest = EVP_sha512();
61         break;
62       default:
63         return false;
64     }
65 
66     return VerifySignedData(digest, public_key_.get(), data, signature);
67   }
68 
GetCommonName() const69   const std::string& GetCommonName() const override { return common_name_; }
70 
71  private:
72   const bssl::UniquePtr<EVP_PKEY> public_key_;
73   const std::string common_name_;
74 };
75 
GetAudioPolicy(const std::vector<X509 * > & path)76 CastDeviceCertPolicy GetAudioPolicy(const std::vector<X509*>& path) {
77   // Cast device certificates use the policy 1.3.6.1.4.1.11129.2.5.2 to indicate
78   // it is *restricted* to an audio-only device whereas the absence of a policy
79   // means it is unrestricted.
80   //
81   // This is somewhat different than RFC 5280's notion of policies, so policies
82   // are checked separately outside of path building.
83   //
84   // See the unit-tests VerifyCastDeviceCertTest.Policies* for some
85   // concrete examples of how this works.
86   //
87   // Iterate over all the certificates, including the root certificate. If any
88   // certificate contains the audio-only policy, the whole chain is considered
89   // constrained to audio-only device certificates.
90   //
91   // Policy mappings are not accounted for. The expectation is that top-level
92   // intermediates issued with audio-only will have no mappings. If subsequent
93   // certificates in the chain do, it won't matter as the chain is already
94   // restricted to being audio-only.
95   CastDeviceCertPolicy policy = CastDeviceCertPolicy::kUnrestricted;
96   const ConstDataSpan& audio_only_policy_oid = AudioOnlyPolicyOid();
97   for (uint32_t i = 0;
98        i < path.size() && policy != CastDeviceCertPolicy::kAudioOnly; ++i) {
99     X509* cert = path[path.size() - 1 - i];
100 
101     // Gets an index into the extension list that points to the
102     // certificatePolicies extension, if it exists, -1 otherwise.
103     int pos = X509_get_ext_by_NID(cert, NID_certificate_policies, -1);
104     if (pos != -1) {
105       X509_EXTENSION* policies_extension = X509_get_ext(cert, pos);
106       const ASN1_STRING* value = X509_EXTENSION_get_data(policies_extension);
107       const uint8_t* in = ASN1_STRING_get0_data(value);
108       CERTIFICATEPOLICIES* policies =
109           d2i_CERTIFICATEPOLICIES(nullptr, &in, ASN1_STRING_length(value));
110 
111       if (policies) {
112         // Check for |audio_only_policy_oid| in the set of policies.
113         uint32_t policy_count = sk_POLICYINFO_num(policies);
114         for (uint32_t i = 0; i < policy_count; ++i) {
115           POLICYINFO* info = sk_POLICYINFO_value(policies, i);
116           if (OBJ_length(info->policyid) == audio_only_policy_oid.length &&
117               memcmp(OBJ_get0_data(info->policyid), audio_only_policy_oid.data,
118                      audio_only_policy_oid.length) == 0) {
119             policy = CastDeviceCertPolicy::kAudioOnly;
120             break;
121           }
122         }
123         CERTIFICATEPOLICIES_free(policies);
124       }
125     }
126   }
127   return policy;
128 }
129 
130 }  // namespace
131 
VerifyDeviceCert(const std::vector<std::string> & der_certs,const DateTime & time,std::unique_ptr<CertVerificationContext> * context,CastDeviceCertPolicy * policy,const CastCRL * crl,CRLPolicy crl_policy,TrustStore * trust_store)132 Error VerifyDeviceCert(const std::vector<std::string>& der_certs,
133                        const DateTime& time,
134                        std::unique_ptr<CertVerificationContext>* context,
135                        CastDeviceCertPolicy* policy,
136                        const CastCRL* crl,
137                        CRLPolicy crl_policy,
138                        TrustStore* trust_store) {
139   if (!trust_store) {
140     trust_store = CastTrustStore::GetInstance()->trust_store();
141   }
142 
143   // Fail early if CRL is required but not provided.
144   if (!crl && crl_policy == CRLPolicy::kCrlRequired) {
145     return Error::Code::kErrCrlInvalid;
146   }
147 
148   CertificatePathResult result_path = {};
149   Error error = FindCertificatePath(der_certs, time, &result_path, trust_store);
150   if (!error.ok()) {
151     return error;
152   }
153 
154   if (crl_policy == CRLPolicy::kCrlRequired &&
155       !crl->CheckRevocation(result_path.path, time)) {
156     return Error::Code::kErrCertsRevoked;
157   }
158 
159   *policy = GetAudioPolicy(result_path.path);
160 
161   // Finally, make sure there is a common name to give to
162   // CertVerificationContextImpl.
163   X509_NAME* target_subject =
164       X509_get_subject_name(result_path.target_cert.get());
165   int len =
166       X509_NAME_get_text_by_NID(target_subject, NID_commonName, nullptr, 0);
167   if (len <= 0) {
168     return Error::Code::kErrCertsRestrictions;
169   }
170   // X509_NAME_get_text_by_NID writes one more byte than it reports, for a
171   // trailing NUL.
172   std::string common_name(len + 1, 0);
173   len = X509_NAME_get_text_by_NID(target_subject, NID_commonName,
174                                   &common_name[0], common_name.size());
175   if (len <= 0) {
176     return Error::Code::kErrCertsRestrictions;
177   }
178   common_name.resize(len);
179 
180   context->reset(new CertVerificationContextImpl(
181       bssl::UniquePtr<EVP_PKEY>{X509_get_pubkey(result_path.target_cert.get())},
182       std::move(common_name)));
183 
184   return Error::Code::kNone;
185 }
186 
187 }  // namespace cast
188 }  // namespace openscreen
189