3dc05867f6d081ed4b166b808ef0a49c20fe220b
[platform/framework/web/crosswalk.git] / src / xwalk / application / common / installer / signature_parser.cc
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.
5
6 #include "xwalk/application/common/installer/signature_parser.h"
7
8 #include <list>
9 #include <set>
10 #include <string>
11 #include <utility>
12 #include <vector>
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"
20
21 namespace {
22 const char kExpectedXmlns[] = "http://www.w3.org/2000/09/xmldsig#";
23 // TAG TOKENS
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";
40
41 // ATTRIBUTE TOKENS
42 const char kTokenAlgorithm[] = "Algorithm";
43 const char kTokenURI[] = "URI";
44 const char kTokenID[] = "Id";
45
46 // ATTRIBUTE VALUES
47 const char kTokenAttrProfile[] = "profile";
48 const char kTokenAttrRole[] = "role";
49 const char kTokenAttrIdentifier[] = "identifier";
50
51 bool TagNameEquals(const xmlNodePtr node,
52     const char* expected_name, const xmlNsPtr expected_namespace) {
53   if (node->ns != expected_namespace)
54     return false;
55
56   return 0 == strcmp(expected_name, reinterpret_cast<const char*>(node->name));
57 }
58
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))
65       continue;
66
67     result.push_back(child);
68   }
69   return result;
70 }
71
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)) {
78       result = child;
79       break;
80     }
81   }
82   return result;
83 }
84
85 // Returns the value of a named attribute, or the empty string.
86 std::string GetAttribute(
87     const xmlNodePtr node, const char* attribute_name) {
88   const xmlChar* 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));
95   }
96   return std::string();
97 }
98
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))
105       return ns;
106   }
107   return NULL;
108 }
109
110 }  // namespace
111
112 namespace xwalk {
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.";
120     return false;
121   }
122
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.";
128     return false;
129   }
130   std::string canonicalization_method =
131     GetAttribute(canonicalization_method_node, kTokenAlgorithm);
132   data->set_canonicalization_method(canonicalization_method);
133
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.";
139     return false;
140   }
141   std::string signature_method =
142     GetAttribute(signature_method_node, kTokenAlgorithm);
143   data->set_signature_method(signature_method);
144
145   // Parse <Reference>
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.";
150     return false;
151   }
152
153   std::string uri, transform_algorithm, digest_method, digest_value;
154   xmlNodePtr refer_node, transforms_node, transform_node, digest_method_node,
155              digest_value_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);
162     if (uri.empty()) {
163       LOG(ERROR) << "Missing URI attribute.";
164       return false;
165     }
166     reference_set.insert(uri);
167
168     // Parse <Transforms>
169     transforms_node =
170       GetFirstChild(refer_node, signature_ns, kTokenTransforms);
171     if (!transforms_node) {
172       transform_node =
173         GetFirstChild(transforms_node, signature_ns, kTokenTransform);
174       if (!transforms_node) {
175         reference_data.transform_algorithm =
176           GetAttribute(transform_node, kTokenAlgorithm);
177       }
178     }
179
180     // Parse <DigestMethod>
181     digest_method_node =
182       GetFirstChild(refer_node, signature_ns, kTokenDigestMethod);
183     if (!digest_method_node) {
184       LOG(ERROR) << "Missing DigestMethod tag.";
185       return false;
186     }
187     reference_data.digest_method =
188       GetAttribute(digest_method_node, kTokenAlgorithm);
189     if (reference_data.digest_method.empty()) {
190       LOG(ERROR) << "Missing DigestMethod attribute.";
191       return false;
192     }
193
194     // Parser <DigestValue>
195     digest_value_node =
196       GetFirstChild(refer_node, signature_ns, kTokenDigestValue);
197     if (!digest_value_node) {
198       LOG(ERROR) << "Missing DigestValue tag.";
199       return false;
200     }
201     reference_data.digest_value =
202       XmlStringToStdString(xmlNodeGetContent(digest_value_node));
203     if (reference_data.digest_value.empty()) {
204       LOG(ERROR) << "Missing DigestValue.";
205       return false;
206     }
207     reference_hash_map.insert(make_pair(uri, reference_data));
208   }
209   data->set_reference_set(reference_set);
210   data->set_reference_hash_map(reference_hash_map);
211
212   return true;
213 }
214
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.";
220     return false;
221   }
222   std::string signature_value = XmlStringToStdString(
223       xmlNodeGetContent(sign_value_node));
224   data->set_signature_value(signature_value);
225   return true;
226 }
227
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.";
233     return true;
234   }
235
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.";
243     return true;
244   }
245
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.";
251     return false;
252   }
253
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)));
260   }
261   data->set_certificate_list(certificate_list);
262   return true;
263 }
264
265 bool ParseObjectElement(
266     const xmlNodePtr node, const xmlNsPtr ns, SignatureData* data) {
267   xmlNodePtr object_node = GetFirstChild(node, ns, kTokenObject);
268   if (!object_node) {
269     LOG(ERROR) << "Missing Object tag.";
270     return false;
271   }
272
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.";
280     return false;
281   }
282
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;
291     if (!child) {
292       LOG(ERROR) << "Failing to find " << element_name
293         << "  element.";
294       return false;
295     }
296
297     if (Id.compare(kTokenAttrProfile) == 0) {
298       profile_uri = GetAttribute(child, kTokenURI);
299       data->set_profile_uri(profile_uri);
300     }
301     if (Id.compare(kTokenAttrRole) == 0) {
302       role_uri = GetAttribute(child, kTokenURI);
303       data->set_role_uri(role_uri);
304     }
305   }
306
307   return true;
308 }
309
310 bool ParseXML(xmlDocPtr docPtr, SignatureData* data) {
311   xmlNodePtr root = xmlDocGetRootElement(docPtr);
312   if (!root) {
313     LOG(ERROR) << "Missinging root node.";
314     return false;
315   }
316
317   // Look for the required namespace declaration.
318   xmlNsPtr signature_ns = GetNamespace(root, kExpectedXmlns);
319   if (!signature_ns) {
320     LOG(ERROR) << "Missinging or incorrect xmlns on signature tag.";
321     return false;
322   }
323   if (!TagNameEquals(root, kTokenSignature, signature_ns)) {
324     LOG(ERROR) << "Missinging Signature tag.";
325     return false;
326   }
327
328   if (!ParseSignedInfoElement(root, signature_ns, data))
329     return false;
330
331   if (!ParseSignatureValueElement(root, signature_ns, data))
332     return false;
333
334   if (!ParseKeyInfoElement(root, signature_ns, data))
335     return false;
336
337   if (!ParseObjectElement(root, signature_ns, data))
338     return false;
339
340   return true;
341 }
342
343 // static
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));
349
350   xmlInitParser();
351   xmlDocPtr doc = xmlParseFile(file_name.c_str());
352   if (!doc) {
353     LOG(ERROR) << "Opening signature " << file_name << " failed.";
354     return scoped_ptr<SignatureData>();
355   }
356
357   if (!ParseXML(doc, data.get())) {
358     LOG(ERROR) << "Parsering failed.";
359     xmlFreeDoc(doc);
360     return scoped_ptr<SignatureData>();
361   }
362
363   xmlFreeDoc(doc);
364   xmlCleanupParser();
365   return data.Pass();
366 }
367 }  // namespace application
368 }  // namespace xwalk