1 // Copyright (c) 2012 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.
5 #include "net/cert/x509_cert_types.h"
7 #include <CoreServices/CoreServices.h>
8 #include <Security/SecAsn1Coder.h>
9 #include <Security/Security.h>
11 #include "base/i18n/icu_string_conversions.h"
12 #include "base/logging.h"
13 #include "base/mac/mac_logging.h"
14 #include "base/strings/utf_string_conversions.h"
20 // The BER encoding of 0.9.2342.19200300.100.1.25.
21 // On 10.6 and later this is available as CSSMOID_DomainComponent, which is an
22 // external symbol from Security.framework. However, it appears that Apple's
23 // implementation improperly encoded this on 10.6+, and even still is
24 // unavailable on 10.5, so simply including the raw BER here.
26 // Note: CSSM is allowed to store CSSM_OIDs in any arbitrary format desired,
27 // as long as the symbols are properly exposed. The fact that Apple's
28 // implementation stores it in BER is an internal implementation detail
29 // observed by studying libsecurity_cssm.
30 const uint8 kDomainComponentData
[] = {
31 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19
34 const CSSM_OID kDomainComponentOID
= {
35 arraysize(kDomainComponentData
),
36 const_cast<uint8
*>(kDomainComponentData
)
39 const CSSM_OID
* kOIDs
[] = {
41 &CSSMOID_LocalityName
,
42 &CSSMOID_StateProvinceName
,
44 &CSSMOID_StreetAddress
,
45 &CSSMOID_OrganizationName
,
46 &CSSMOID_OrganizationalUnitName
,
50 // The following structs and templates work with Apple's very arcane and under-
51 // documented SecAsn1Parser API, which is apparently the same as NSS's ASN.1
53 // http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn1.html
55 // These are used to parse the contents of a raw
56 // BER DistinguishedName structure.
58 const SecAsn1Template kStringValueTemplate
[] = {
59 { SEC_ASN1_CHOICE
, offsetof(CSSM_X509_TYPE_VALUE_PAIR
, valueType
), },
60 { SEC_ASN1_PRINTABLE_STRING
,
61 offsetof(CSSM_X509_TYPE_VALUE_PAIR
, value
), 0,
62 BER_TAG_PRINTABLE_STRING
},
63 { SEC_ASN1_IA5_STRING
,
64 offsetof(CSSM_X509_TYPE_VALUE_PAIR
, value
), 0,
66 { SEC_ASN1_T61_STRING
,
67 offsetof(CSSM_X509_TYPE_VALUE_PAIR
, value
), 0,
69 { SEC_ASN1_UTF8_STRING
,
70 offsetof(CSSM_X509_TYPE_VALUE_PAIR
, value
), 0,
71 BER_TAG_PKIX_UTF8_STRING
},
72 { SEC_ASN1_BMP_STRING
,
73 offsetof(CSSM_X509_TYPE_VALUE_PAIR
, value
), 0,
74 BER_TAG_PKIX_BMP_STRING
},
75 { SEC_ASN1_UNIVERSAL_STRING
,
76 offsetof(CSSM_X509_TYPE_VALUE_PAIR
, value
), 0,
77 BER_TAG_PKIX_UNIVERSAL_STRING
},
81 const SecAsn1Template kKeyValuePairTemplate
[] = {
82 { SEC_ASN1_SEQUENCE
, 0, NULL
, sizeof(CSSM_X509_TYPE_VALUE_PAIR
) },
83 { SEC_ASN1_OBJECT_ID
, offsetof(CSSM_X509_TYPE_VALUE_PAIR
, type
), },
84 { SEC_ASN1_INLINE
, 0, &kStringValueTemplate
, },
88 struct KeyValuePairs
{
89 CSSM_X509_TYPE_VALUE_PAIR
* pairs
;
92 const SecAsn1Template kKeyValuePairSetTemplate
[] = {
93 { SEC_ASN1_SET_OF
, offsetof(KeyValuePairs
, pairs
),
94 kKeyValuePairTemplate
, sizeof(KeyValuePairs
) }
98 KeyValuePairs
** pairs_list
;
101 const SecAsn1Template kNameTemplate
[] = {
102 { SEC_ASN1_SEQUENCE_OF
, offsetof(X509Name
, pairs_list
),
103 kKeyValuePairSetTemplate
, sizeof(X509Name
) }
106 // Converts raw CSSM_DATA to a std::string. (Char encoding is unaltered.)
107 std::string
DataToString(CSSM_DATA data
) {
109 reinterpret_cast<std::string::value_type
*>(data
.Data
),
113 // Converts raw CSSM_DATA in ISO-8859-1 to a std::string in UTF-8.
114 std::string
Latin1DataToUTF8String(CSSM_DATA data
) {
115 base::string16 utf16
;
116 if (!CodepageToUTF16(DataToString(data
), base::kCodepageLatin1
,
117 base::OnStringConversionError::FAIL
, &utf16
))
119 return base::UTF16ToUTF8(utf16
);
122 // Converts big-endian UTF-16 to UTF-8 in a std::string.
123 // Note: The byte-order flipping is done in place on the input buffer!
124 bool UTF16BigEndianToUTF8(base::char16
* chars
, size_t length
,
125 std::string
* out_string
) {
126 for (size_t i
= 0; i
< length
; i
++)
127 chars
[i
] = EndianU16_BtoN(chars
[i
]);
128 return base::UTF16ToUTF8(chars
, length
, out_string
);
131 // Converts big-endian UTF-32 to UTF-8 in a std::string.
132 // Note: The byte-order flipping is done in place on the input buffer!
133 bool UTF32BigEndianToUTF8(char32
* chars
, size_t length
,
134 std::string
* out_string
) {
135 for (size_t i
= 0; i
< length
; ++i
)
136 chars
[i
] = EndianS32_BtoN(chars
[i
]);
137 #if defined(WCHAR_T_IS_UTF32)
138 return base::WideToUTF8(reinterpret_cast<const wchar_t*>(chars
),
141 #error This code doesn't handle 16-bit wchar_t.
145 // Adds a type+value pair to the appropriate vector from a C array.
146 // The array is keyed by the matching OIDs from kOIDS[].
147 void AddTypeValuePair(const CSSM_OID type,
148 const std::string& value,
149 std::vector<std::string>* values[]) {
150 for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) {
151 if (CSSMOIDEqual(&type, kOIDs[oid])) {
152 values[oid]->push_back(value);
158 // Stores the first string of the vector, if any, to *single_value.
159 void SetSingle(const std::vector<std::string>& values,
160 std::string* single_value) {
161 // We don't expect to have more than one CN, L, S, and C.
162 LOG_IF(WARNING
, values
.size() > 1) << "Didn't expect multiple values";
164 *single_value
= values
[0];
167 bool match(const std::string
& str
, const std::string
& against
) {
168 // TODO(snej): Use the full matching rules specified in RFC 5280 sec. 7.1
169 // including trimming and case-folding: <http://www.ietf.org/rfc/rfc5280.txt>.
170 return against
== str
;
173 bool match(const std::vector
<std::string
>& rdn1
,
174 const std::vector
<std::string
>& rdn2
) {
175 // "Two relative distinguished names RDN1 and RDN2 match if they have the
176 // same number of naming attributes and for each naming attribute in RDN1
177 // there is a matching naming attribute in RDN2." --RFC 5280 sec. 7.1.
178 if (rdn1
.size() != rdn2
.size())
180 for (unsigned i1
= 0; i1
< rdn1
.size(); ++i1
) {
182 for (i2
= 0; i2
< rdn2
.size(); ++i2
) {
183 if (match(rdn1
[i1
], rdn2
[i2
]))
186 if (i2
== rdn2
.size())
194 bool CertPrincipal::ParseDistinguishedName(const void* ber_name_data
,
196 DCHECK(ber_name_data
);
198 // First parse the BER |name_data| into the above structs.
199 SecAsn1CoderRef coder
= NULL
;
200 SecAsn1CoderCreate(&coder
);
202 X509Name
* name
= NULL
;
203 OSStatus err
= SecAsn1Decode(coder
, ber_name_data
, length
, kNameTemplate
,
206 OSSTATUS_LOG(ERROR
, err
) << "SecAsn1Decode";
207 SecAsn1CoderRelease(coder
);
211 // Now scan the structs and add the values to my string vectors.
212 // I don't store multiple common/locality/state/country names, so use
213 // temporary vectors for those.
214 std::vector
<std::string
> common_names
, locality_names
, state_names
,
216 std::vector
<std::string
>* values
[] = {
217 &common_names
, &locality_names
,
218 &state_names
, &country_names
,
219 &this->street_addresses
,
220 &this->organization_names
,
221 &this->organization_unit_names
,
222 &this->domain_components
224 DCHECK(arraysize(kOIDs
) == arraysize(values
));
226 for (int rdn
= 0; name
[rdn
].pairs_list
; ++rdn
) {
227 CSSM_X509_TYPE_VALUE_PAIR
* pair
;
228 for (int pair_index
= 0;
229 NULL
!= (pair
= name
[rdn
].pairs_list
[0][pair_index
].pairs
);
231 switch (pair
->valueType
) {
232 case BER_TAG_IA5_STRING
: // ASCII (that means 7-bit!)
233 case BER_TAG_PRINTABLE_STRING
: // a subset of ASCII
234 case BER_TAG_PKIX_UTF8_STRING
: // UTF-8
235 AddTypeValuePair(pair
->type
, DataToString(pair
->value
), values
);
237 case BER_TAG_T61_STRING
: // T61, pretend it's Latin-1
238 AddTypeValuePair(pair
->type
,
239 Latin1DataToUTF8String(pair
->value
),
242 case BER_TAG_PKIX_BMP_STRING
: { // UTF-16, big-endian
244 UTF16BigEndianToUTF8(
245 reinterpret_cast<base::char16
*>(pair
->value
.Data
),
246 pair
->value
.Length
/ sizeof(base::char16
),
248 AddTypeValuePair(pair
->type
, value
, values
);
251 case BER_TAG_PKIX_UNIVERSAL_STRING
: { // UTF-32, big-endian
253 UTF32BigEndianToUTF8(reinterpret_cast<char32
*>(pair
->value
.Data
),
254 pair
->value
.Length
/ sizeof(char32
),
256 AddTypeValuePair(pair
->type
, value
, values
);
260 DCHECK_EQ(pair
->valueType
, BER_TAG_UNKNOWN
);
261 // We don't know what data type this is, but we'll store it as a blob.
262 // Displaying the string may not work, but at least it can be compared
263 // byte-for-byte by a Matches() call.
264 AddTypeValuePair(pair
->type
, DataToString(pair
->value
), values
);
270 SetSingle(common_names
, &this->common_name
);
271 SetSingle(locality_names
, &this->locality_name
);
272 SetSingle(state_names
, &this->state_or_province_name
);
273 SetSingle(country_names
, &this->country_name
);
275 // Releasing |coder| frees all the memory pointed to via |name|.
276 SecAsn1CoderRelease(coder
);
280 bool CertPrincipal::Matches(const CertPrincipal
& against
) const {
281 return match(common_name
, against
.common_name
) &&
282 match(locality_name
, against
.locality_name
) &&
283 match(state_or_province_name
, against
.state_or_province_name
) &&
284 match(country_name
, against
.country_name
) &&
285 match(street_addresses
, against
.street_addresses
) &&
286 match(organization_names
, against
.organization_names
) &&
287 match(organization_unit_names
, against
.organization_unit_names
) &&
288 match(domain_components
, against
.domain_components
);