blob: 7fa5dacf0605f8e859348be5555dfb5160f5da67 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include "net/cert/internal/certificate_policies.h"
#include "net/der/input.h"
#include "net/der/parser.h"
#include "net/der/tag.h"
namespace net {
namespace {
// -- policyQualifierIds for Internet policy qualifiers
//
// id-qt OBJECT IDENTIFIER ::= { id-pkix 2 }
// id-qt-cps OBJECT IDENTIFIER ::= { id-qt 1 }
//
// In dotted decimal form: 1.3.6.1.5.5.7.2.1
const der::Input CpsPointerId() {
static const uint8_t cps_pointer_id[] = {0x2b, 0x06, 0x01, 0x05,
0x05, 0x07, 0x02, 0x01};
return der::Input(cps_pointer_id);
}
// id-qt-unotice OBJECT IDENTIFIER ::= { id-qt 2 }
//
// In dotted decimal form: 1.3.6.1.5.5.7.2.2
const der::Input UserNoticeId() {
static const uint8_t user_notice_id[] = {0x2b, 0x06, 0x01, 0x05,
0x05, 0x07, 0x02, 0x02};
return der::Input(user_notice_id);
}
// Ignores the policyQualifiers, but does some minimal correctness checking.
// TODO(mattm): parse and return the policyQualifiers, since the cert viewer
// still needs to display them.
bool ParsePolicyQualifiers(const der::Input& policy_oid,
der::Parser* policy_qualifiers_sequence_parser) {
// If it is present, the policyQualifiers sequence should have at least 1
// element.
if (!policy_qualifiers_sequence_parser->HasMore())
return false;
while (policy_qualifiers_sequence_parser->HasMore()) {
der::Parser policy_information_parser;
if (!policy_qualifiers_sequence_parser->ReadSequence(
&policy_information_parser)) {
return false;
}
der::Input qualifier_oid;
if (!policy_information_parser.ReadTag(der::kOid, &qualifier_oid))
return false;
// RFC 5280 section 4.2.1.4: When qualifiers are used with the special
// policy anyPolicy, they MUST be limited to the qualifiers identified in
// this section.
if (policy_oid == AnyPolicy() && qualifier_oid != CpsPointerId() &&
qualifier_oid != UserNoticeId()) {
return false;
}
der::Tag tag;
der::Input value;
if (!policy_information_parser.ReadTagAndValue(&tag, &value))
return false;
// Should not have trailing data after qualifier.
if (policy_information_parser.HasMore())
return false;
}
return true;
}
} // namespace
const der::Input AnyPolicy() {
// id-ce OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 29}
//
// id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 }
//
// anyPolicy OBJECT IDENTIFIER ::= { id-ce-certificatePolicies 0 }
//
// In dotted decimal form: 2.5.29.32.0
static const uint8_t any_policy[] = {0x55, 0x1D, 0x20, 0x00};
return der::Input(any_policy);
}
// RFC 5280 section 4.2.1.4. Certificate Policies:
//
// certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
//
// PolicyInformation ::= SEQUENCE {
// policyIdentifier CertPolicyId,
// policyQualifiers SEQUENCE SIZE (1..MAX) OF
// PolicyQualifierInfo OPTIONAL }
//
// CertPolicyId ::= OBJECT IDENTIFIER
//
// PolicyQualifierInfo ::= SEQUENCE {
// policyQualifierId PolicyQualifierId,
// qualifier ANY DEFINED BY policyQualifierId }
//
// PolicyQualifierId ::= OBJECT IDENTIFIER ( id-qt-cps | id-qt-unotice )
//
// Qualifier ::= CHOICE {
// cPSuri CPSuri,
// userNotice UserNotice }
//
// CPSuri ::= IA5String
//
// UserNotice ::= SEQUENCE {
// noticeRef NoticeReference OPTIONAL,
// explicitText DisplayText OPTIONAL }
//
// NoticeReference ::= SEQUENCE {
// organization DisplayText,
// noticeNumbers SEQUENCE OF INTEGER }
//
// DisplayText ::= CHOICE {
// ia5String IA5String (SIZE (1..200)),
// visibleString VisibleString (SIZE (1..200)),
// bmpString BMPString (SIZE (1..200)),
// utf8String UTF8String (SIZE (1..200)) }
bool ParseCertificatePoliciesExtension(const der::Input& extension_value,
std::vector<der::Input>* policies) {
der::Parser extension_parser(extension_value);
der::Parser policies_sequence_parser;
if (!extension_parser.ReadSequence(&policies_sequence_parser))
return false;
// Should not have trailing data after certificatePolicies sequence.
if (extension_parser.HasMore())
return false;
// The certificatePolicies sequence should have at least 1 element.
if (!policies_sequence_parser.HasMore())
return false;
policies->clear();
while (policies_sequence_parser.HasMore()) {
der::Parser policy_information_parser;
if (!policies_sequence_parser.ReadSequence(&policy_information_parser))
return false;
der::Input policy_oid;
if (!policy_information_parser.ReadTag(der::kOid, &policy_oid))
return false;
// Build the |policies| vector in sorted order (sorted on DER encoded policy
// OID). Use a binary search to check whether a duplicate policy is present,
// and if not, where to insert the policy to maintain the sorted order.
std::vector<der::Input>::iterator i =
std::lower_bound(policies->begin(), policies->end(), policy_oid);
// RFC 5280 section 4.2.1.4: A certificate policy OID MUST NOT appear more
// than once in a certificate policies extension.
if (i != policies->end() && *i == policy_oid)
return false;
policies->insert(i, policy_oid);
if (!policy_information_parser.HasMore())
continue;
der::Parser policy_qualifiers_sequence_parser;
if (!policy_information_parser.ReadSequence(
&policy_qualifiers_sequence_parser)) {
return false;
}
// Should not have trailing data after policyQualifiers sequence.
if (policy_information_parser.HasMore())
return false;
if (!ParsePolicyQualifiers(policy_oid, &policy_qualifiers_sequence_parser))
return false;
}
return true;
}
} // namespace net