1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Copyright (c) 2014 Intel Corporation.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
6 #include "xwalk/application/common/installer/signature_parser.h"
13 #include "base/file_util.h"
14 #include "base/logging.h"
15 #include "libxml/parser.h"
16 #include "libxml/xmlschemas.h"
17 #include "libxml/xpathInternals.h"
18 #include "libxml/xmlreader.h"
19 #include "third_party/libxml/chromium/libxml_utils.h"
22 const char kExpectedXmlns[] = "http://www.w3.org/2000/09/xmldsig#";
24 const char kTokenSignature[] = "Signature";
25 const char kTokenSignedInfo[] = "SignedInfo";
26 const char kTokenCanonicalizationMethod[] = "CanonicalizationMethod";
27 const char kTokenSignatureMethod[] = "SignatureMethod";
28 const char kTokenReference[] = "Reference";
29 const char kTokenTransforms[] = "Transforms";
30 const char kTokenTransform[] = "Transform";
31 const char kTokenDigestMethod[] = "DigestMethod";
32 const char kTokenDigestValue[] = "DigestValue";
33 const char kTokenSignatureValue[] = "SignatureValue";
34 const char kTokenkeyInfo[] = "KeyInfo";
35 const char kTokenX509Data[] = "X509Data";
36 const char kTokenX509Certificate[] = "X509Certificate";
37 const char kTokenObject[] = "Object";
38 const char kTokenSignatureProperties[] = "SignatureProperties";
39 const char kTokenSignatureProperty[] = "SignatureProperty";
42 const char kTokenAlgorithm[] = "Algorithm";
43 const char kTokenURI[] = "URI";
44 const char kTokenID[] = "Id";
47 const char kTokenAttrProfile[] = "profile";
48 const char kTokenAttrRole[] = "role";
49 const char kTokenAttrIdentifier[] = "identifier";
51 bool TagNameEquals(const xmlNodePtr node,
52 const char* expected_name, const xmlNsPtr expected_namespace) {
53 if (node->ns != expected_namespace)
56 return 0 == strcmp(expected_name, reinterpret_cast<const char*>(node->name));
59 // Returns child nodes of |root| with name |name|.
60 std::vector<xmlNodePtr> GetChildren(
61 const xmlNodePtr root, const xmlNsPtr xml_namespace, const char* name) {
62 std::vector<xmlNodePtr> result;
63 for (xmlNodePtr child = root->children; child != NULL; child = child->next) {
64 if (!TagNameEquals(child, name, xml_namespace))
67 result.push_back(child);
72 // Returns the first child node of |root| with name |name|.
73 xmlNodePtr GetFirstChild(
74 const xmlNodePtr root, const xmlNsPtr xml_namespace, const char* name) {
75 xmlNodePtr result = NULL;
76 for (xmlNodePtr child = root->children; child != NULL; child = child->next) {
77 if (TagNameEquals(child, name, xml_namespace)) {
85 // Returns the value of a named attribute, or the empty string.
86 std::string GetAttribute(
87 const xmlNodePtr node, const char* attribute_name) {
89 reinterpret_cast<const xmlChar*>(attribute_name);
90 for (xmlAttr* attr = node->properties; attr != NULL; attr = attr->next) {
91 if (!xmlStrcmp(attr->name, name) && attr->children &&
92 attr->children->content)
93 return std::string(reinterpret_cast<const char*>(
94 attr->children->content));
99 // Returns a pointer to the xmlNs on |node| with the |expected_href|, or
100 // NULL if there isn't one with that href.
101 xmlNsPtr GetNamespace(const xmlNodePtr node, const char* expected_href) {
102 const xmlChar* href = reinterpret_cast<const xmlChar*>(expected_href);
103 for (xmlNsPtr ns = node->ns; ns != NULL; ns = ns->next) {
104 if (ns->href && !xmlStrcmp(ns->href, href))
113 namespace application {
114 bool ParseSignedInfoElement(
115 const xmlNodePtr node, const xmlNsPtr signature_ns, SignatureData* data) {
116 xmlNodePtr signed_info_node =
117 GetFirstChild(node, signature_ns, kTokenSignedInfo);
118 if (!signed_info_node) {
119 LOG(ERROR) << "Missing SignedInfo tag.";
123 // Parse <CanonicalizationMethod>
124 xmlNodePtr canonicalization_method_node =
125 GetFirstChild(signed_info_node, signature_ns, kTokenCanonicalizationMethod);
126 if (!canonicalization_method_node) {
127 LOG(ERROR) << "Missing SignedInfo tag.";
130 std::string canonicalization_method =
131 GetAttribute(canonicalization_method_node, kTokenAlgorithm);
132 data->set_canonicalization_method(canonicalization_method);
134 // Parse <SignatureMethod>
135 xmlNodePtr signature_method_node =
136 GetFirstChild(signed_info_node, signature_ns, kTokenSignatureMethod);
137 if (!signature_method_node) {
138 LOG(ERROR) << "Missing SignatureMethod tag.";
141 std::string signature_method =
142 GetAttribute(signature_method_node, kTokenAlgorithm);
143 data->set_signature_method(signature_method);
146 std::vector<xmlNodePtr> reference_vec =
147 GetChildren(signed_info_node, signature_ns, kTokenReference);
148 if (reference_vec.empty()) {
149 LOG(ERROR) << "Missing Reference tag.";
153 std::string uri, transform_algorithm, digest_method, digest_value;
154 xmlNodePtr refer_node, transforms_node, transform_node, digest_method_node,
156 ReferenceData reference_data;
157 std::set<std::string> reference_set;
158 ReferenceHashMap reference_hash_map;
159 for (size_t i = 0; i < reference_vec.size(); ++i) {
160 refer_node = reference_vec[i];
161 uri = GetAttribute(refer_node, kTokenURI);
163 LOG(ERROR) << "Missing URI attribute.";
166 reference_set.insert(uri);
168 // Parse <Transforms>
170 GetFirstChild(refer_node, signature_ns, kTokenTransforms);
171 if (!transforms_node) {
173 GetFirstChild(transforms_node, signature_ns, kTokenTransform);
174 if (!transforms_node) {
175 reference_data.transform_algorithm =
176 GetAttribute(transform_node, kTokenAlgorithm);
180 // Parse <DigestMethod>
182 GetFirstChild(refer_node, signature_ns, kTokenDigestMethod);
183 if (!digest_method_node) {
184 LOG(ERROR) << "Missing DigestMethod tag.";
187 reference_data.digest_method =
188 GetAttribute(digest_method_node, kTokenAlgorithm);
189 if (reference_data.digest_method.empty()) {
190 LOG(ERROR) << "Missing DigestMethod attribute.";
194 // Parser <DigestValue>
196 GetFirstChild(refer_node, signature_ns, kTokenDigestValue);
197 if (!digest_value_node) {
198 LOG(ERROR) << "Missing DigestValue tag.";
201 reference_data.digest_value =
202 XmlStringToStdString(xmlNodeGetContent(digest_value_node));
203 if (reference_data.digest_value.empty()) {
204 LOG(ERROR) << "Missing DigestValue.";
207 reference_hash_map.insert(make_pair(uri, reference_data));
209 data->set_reference_set(reference_set);
210 data->set_reference_hash_map(reference_hash_map);
215 bool ParseSignatureValueElement(
216 const xmlNodePtr node, const xmlNsPtr ns, SignatureData* data) {
217 xmlNodePtr sign_value_node = GetFirstChild(node, ns, kTokenSignatureValue);
218 if (!sign_value_node) {
219 LOG(ERROR) << "Missing SignatureValue tag.";
222 std::string signature_value = XmlStringToStdString(
223 xmlNodeGetContent(sign_value_node));
224 data->set_signature_value(signature_value);
228 bool ParseKeyInfoElement(
229 const xmlNodePtr node, const xmlNsPtr ns, SignatureData* data) {
230 xmlNodePtr key_info_node = GetFirstChild(node, ns, kTokenkeyInfo);
231 if (!key_info_node) {
232 LOG(INFO) << "Missing KeyInfo tag, it is allowed by schema.xsd.";
236 // KeyInfo may contain keys, names, certificates and other public key
237 // management. Now I only handle X509 certifcates which is commonly used.
238 // TODO(Xu): Other types of element
239 xmlNodePtr X509_data_node =
240 GetFirstChild(key_info_node, ns, kTokenX509Data);
241 if (!X509_data_node) {
242 LOG(INFO) << "Missing X509Data tag.";
246 // Parse <X509Certificate>
247 std::vector<xmlNodePtr> cert_vec =
248 GetChildren(X509_data_node, ns, kTokenX509Certificate);
249 if (cert_vec.empty()) {
250 LOG(ERROR) << "Missing X509Certificate tag.";
254 std::list<std::string> certificate_list;
255 for (std::vector<xmlNode*>::iterator it = cert_vec.begin();
256 it != cert_vec.end(); ++it) {
257 xmlNodePtr certificate_node = *it;
258 certificate_list.push_back(
259 XmlStringToStdString(xmlNodeGetContent(certificate_node)));
261 data->set_certificate_list(certificate_list);
265 bool ParseObjectElement(
266 const xmlNodePtr node, const xmlNsPtr ns, SignatureData* data) {
267 xmlNodePtr object_node = GetFirstChild(node, ns, kTokenObject);
269 LOG(ERROR) << "Missing Object tag.";
273 std::string object_id = GetAttribute(object_node, kTokenID);
274 data->set_object_id(object_id);
275 // Parse <SignatureProperties>
276 xmlNodePtr properties_node =
277 GetFirstChild(object_node, ns, kTokenSignatureProperties);
278 if (!properties_node) {
279 LOG(ERROR) << "Missing Object tag.";
283 std::vector<xmlNodePtr> prop_vec =
284 GetChildren(properties_node, ns, kTokenSignatureProperty);
285 std::string Id, uri, element_name, profile_uri, role_uri;
286 xmlNodePtr sign_property_node, child;
287 for (size_t i = 0; i < prop_vec.size(); i++) {
288 sign_property_node = prop_vec[i];
289 Id = GetAttribute(sign_property_node, kTokenID);
290 child = sign_property_node->children;
292 LOG(ERROR) << "Failing to find " << element_name
297 if (Id.compare(kTokenAttrProfile) == 0) {
298 profile_uri = GetAttribute(child, kTokenURI);
299 data->set_profile_uri(profile_uri);
301 if (Id.compare(kTokenAttrRole) == 0) {
302 role_uri = GetAttribute(child, kTokenURI);
303 data->set_role_uri(role_uri);
310 bool ParseXML(xmlDocPtr docPtr, SignatureData* data) {
311 xmlNodePtr root = xmlDocGetRootElement(docPtr);
313 LOG(ERROR) << "Missinging root node.";
317 // Look for the required namespace declaration.
318 xmlNsPtr signature_ns = GetNamespace(root, kExpectedXmlns);
320 LOG(ERROR) << "Missinging or incorrect xmlns on signature tag.";
323 if (!TagNameEquals(root, kTokenSignature, signature_ns)) {
324 LOG(ERROR) << "Missinging Signature tag.";
328 if (!ParseSignedInfoElement(root, signature_ns, data))
331 if (!ParseSignatureValueElement(root, signature_ns, data))
334 if (!ParseKeyInfoElement(root, signature_ns, data))
337 if (!ParseObjectElement(root, signature_ns, data))
344 scoped_ptr<SignatureData> SignatureParser::CreateSignatureData(
345 const base::FilePath& signature_path, int signature_number) {
346 std::string file_name = signature_path.MaybeAsASCII();
347 scoped_ptr<SignatureData>
348 data(new SignatureData(file_name, signature_number));
351 xmlDocPtr doc = xmlParseFile(file_name.c_str());
353 LOG(ERROR) << "Opening signature " << file_name << " failed.";
354 return scoped_ptr<SignatureData>();
357 if (!ParseXML(doc, data.get())) {
358 LOG(ERROR) << "Parsering failed.";
360 return scoped_ptr<SignatureData>();
367 } // namespace application