1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
|
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/cert/qwac.h"
#include "base/containers/contains.h"
#include "base/logging.h"
#include "third_party/boringssl/src/pki/parser.h"
namespace net {
// RFC 3739 A.1:
//
// QCStatements ::= SEQUENCE OF QCStatement
//
// QCStatement ::= SEQUENCE {
// statementId OBJECT IDENTIFIER,
// statementInfo ANY DEFINED BY statementId OPTIONAL}
std::optional<std::vector<QcStatement>> ParseQcStatements(
bssl::der::Input extension_value) {
std::vector<QcStatement> results;
bssl::der::Parser parser(extension_value);
bssl::der::Parser statements_parser;
if (!parser.ReadSequence(&statements_parser)) {
return std::nullopt;
}
while (statements_parser.HasMore()) {
bssl::der::Parser statement_parser;
if (!statements_parser.ReadSequence(&statement_parser)) {
return std::nullopt;
}
bssl::der::Input statement_id;
if (!statement_parser.ReadTag(CBS_ASN1_OBJECT, &statement_id)) {
return std::nullopt;
}
bssl::der::Input statement_info;
if (statement_parser.HasMore()) {
if (!statement_parser.ReadRawTLV(&statement_info)) {
return std::nullopt;
}
}
if (statement_parser.HasMore()) {
return std::nullopt;
}
results.emplace_back(statement_id, statement_info);
}
return results;
}
std::optional<std::vector<bssl::der::Input>> ParseQcTypeInfo(
bssl::der::Input statement_info) {
// QcType::= SEQUENCE OF OBJECT IDENTIFIER (id-etsi-qct-esign |
// id-etsi-qct-eseal | id-etsi-qct-web, ...)
bssl::der::Parser info_parser(statement_info);
bssl::der::Parser qctype_parser;
if (!info_parser.ReadSequence(&qctype_parser)) {
return std::nullopt;
}
std::vector<bssl::der::Input> results;
while (qctype_parser.HasMore()) {
bssl::der::Input qctype_id;
if (!qctype_parser.ReadTag(CBS_ASN1_OBJECT, &qctype_id)) {
return std::nullopt;
}
results.push_back(qctype_id);
}
if (info_parser.HasMore()) {
return std::nullopt;
}
return results;
}
QwacQcStatementsStatus HasQwacQcStatements(
const std::vector<QcStatement>& qc_statements) {
// ETSI TS 119 411-5 - V2.1.1 - section 6.1.2:
// the QWAC includes QCStatements as specified in clause 4.2 of ETSI EN 319
// 412-4 [4]
//
// ETSI EN 319 412-4 - V1.3.2 - section 4.2:
// QCS-4.2-1: When certificates are issued as EU Qualified Certificates,
// they shall include QCStatements as specified in clauses 4 and 5 of ETSI
// EN 319 412-5 [1].
//
// ETSI EN 319 412-5 - V2.4.1 - section 5:
// clause 4.2.1, statement esi4-qcStatement-1, is Mandatory
//
// ETSI EN 319 412-5 - V2.4.1 - section 4.2.1:
// esi4-qcStatement-1 QC-STATEMENT ::=
// { IDENTIFIED BY id-etsi-qcs-QcCompliance }
// id-etsi-qcs-QcCompliance OBJECT IDENTIFIER ::= { id-etsi-qcs 1 }
// The precise meaning of this statement is enhanced by:
// a) the QC type statement defined in clause 4.2.3 according to table 1
//
// ETSI EN 319 412-5 - V2.4.1 - section 4.2.3:
// This QCStatement declares that a certificate is issued as one and only
// one of the purposes of electronic signature, electronic seal or web site
// authentication
// ...
// id-etsi-qct-web OBJECT IDENTIFIER ::= { id-etsi-qcs-QcType 3 }
// -- Certificate for website authentication as defined in Regulation (EU)
// No 910/2014
bool has_qc_compliance = false;
bool has_qctype_web = false;
for (const auto& statement : qc_statements) {
if (statement.id == bssl::der::Input(kEtsiQcsQcComplianceOid)) {
has_qc_compliance = true;
} else if (statement.id == bssl::der::Input(kEtsiQcsQcTypeOid)) {
std::optional<std::vector<bssl::der::Input>> qc_types =
ParseQcTypeInfo(statement.info);
if (!qc_types.has_value()) {
return QwacQcStatementsStatus::kNotQwac;
}
for (const auto& qc_type_id : qc_types.value()) {
if (qc_type_id == bssl::der::Input(kEtsiQctWebOid)) {
has_qctype_web = true;
}
}
}
}
if (has_qc_compliance && has_qctype_web) {
return QwacQcStatementsStatus::kHasQwacStatements;
} else if (has_qc_compliance || has_qctype_web) {
return QwacQcStatementsStatus::kInconsistent;
}
return QwacQcStatementsStatus::kNotQwac;
}
QwacPoliciesStatus Has1QwacPolicies(
const std::set<bssl::der::Input>& policy_set) {
// ETSI TS 119 411-5 - V2.1.1 - section 4.1.1:
// The 1-QWAC certificate shall be issued in accordance with one of the
// following certificate policies as specified in ETSI EN 319 411-2 [3]:
// a)QEVCP-w; or
// b)QNCP-w.
//
// ETSI EN 319 411-2 - V2.6.0 - section 4.2.2:
// 5) A policy for EU qualified website certificates (QEVCP-w) that
// conforms to the latest version of EVCG [i.7], offering at a minimum the
// "Extended Validated" level of assurance as defined by the CA/Browser
// Forum, and the level of quality defined in Regulation (EU) No 910/2014
// [i.1] for EU qualified certificates used in support of websites
// authentication
//
// 6) A policy for EU qualified website certificates (QNCP-w) that conforms
// to the latest version of BRG [i.3], offering at a minimum the
// "Organization Validated" or "Individual Validated" level of assurance as
// defined by the CA/Browser Forum and the level of quality defined in
// Regulation (EU) No 910/2014 [i.1] for EU qualified certificates used in
// support of websites authentication
const bool has_ev = policy_set.contains(bssl::der::Input(kCabfBrEvOid));
const bool has_iv = policy_set.contains(bssl::der::Input(kCabfBrIvOid));
const bool has_ov = policy_set.contains(bssl::der::Input(kCabfBrOvOid));
const bool has_qevcpw = policy_set.contains(bssl::der::Input(kQevcpwOid));
const bool has_qncpw = policy_set.contains(bssl::der::Input(kQncpwOid));
if (has_ev && has_qevcpw) {
return QwacPoliciesStatus::kHasQwacPolicies;
} else if ((has_ov || has_iv) && has_qncpw) {
return QwacPoliciesStatus::kHasQwacPolicies;
} else if (has_qevcpw || has_qncpw) {
return QwacPoliciesStatus::kInconsistent;
}
return QwacPoliciesStatus::kNotQwac;
}
QwacPoliciesStatus Has2QwacPolicies(
const std::set<bssl::der::Input>& policy_set) {
// ETSI TS 119 411-5 V2.1.1 - 4.2.1:
// The 2-QWAC certificate shall be issued in accordance with the QNCP-w-gen
// certificate policy
//
// ETSI EN 319 411-2 - V2.6.1 - section 4.2.2:
// A policy for EU qualified website certificates (QNCP-w-gen) offering the
// level of quality defined in Regulation (EU) No 910/2014 [i.1] for EU
// qualified certificates used in support of websites authentication for
// general purpose certificate for qualified website authentication
return policy_set.contains(bssl::der::Input(kQncpwgenOid))
? QwacPoliciesStatus::kHasQwacPolicies
: QwacPoliciesStatus::kNotQwac;
}
QwacEkuStatus Has2QwacEku(const bssl::ParsedCertificate* cert) {
// ETSI TS 119 411-5 V2.1.1 - 4.2.2:
// the extKeyUsage value shall only assert the extendedKeyUsage purpose of
// id-kp-tls-binding as specified in Annex A.
if (!cert->has_extended_key_usage()) {
return QwacEkuStatus::kNotQwac;
}
if (!base::Contains(cert->extended_key_usage(),
bssl::der::Input(kIdKpTlsBinding))) {
return QwacEkuStatus::kNotQwac;
}
if (cert->extended_key_usage().size() != 1) {
return QwacEkuStatus::kInconsistent;
;
}
return QwacEkuStatus::kHasQwacEku;
}
} // namespace net
|