[Tizen] Add url decoding for 'uri' attribute of signatures
[platform/framework/web/crosswalk.git] / src / xwalk / application / common / tizen / 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/tizen/signature_parser.h"
7
8 #include <list>
9 #include <set>
10 #include <string>
11 #include <utility>
12 #include <vector>
13 #include "base/files/file_util.h"
14 #include "base/logging.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "libxml/parser.h"
18 #include "libxml/xmlschemas.h"
19 #include "libxml/xpathInternals.h"
20 #include "libxml/xmlreader.h"
21 #include "third_party/libxml/chromium/libxml_utils.h"
22 #include "url/url_util.h"
23
24 namespace {
25 const char kExpectedXmlns[] = "http://www.w3.org/2000/09/xmldsig#";
26 // TAG TOKENS
27 const char kTokenSignature[] = "Signature";
28 const char kTokenSignedInfo[] = "SignedInfo";
29 const char kTokenCanonicalizationMethod[] = "CanonicalizationMethod";
30 const char kTokenSignatureMethod[] = "SignatureMethod";
31 const char kTokenReference[] = "Reference";
32 const char kTokenTransforms[] = "Transforms";
33 const char kTokenTransform[] = "Transform";
34 const char kTokenDigestMethod[] = "DigestMethod";
35 const char kTokenDigestValue[] = "DigestValue";
36 const char kTokenSignatureValue[] = "SignatureValue";
37 const char kTokenkeyInfo[] = "KeyInfo";
38 const char kTokenX509Data[] = "X509Data";
39 const char kTokenX509Certificate[] = "X509Certificate";
40 const char kTokenObject[] = "Object";
41 const char kTokenSignatureProperties[] = "SignatureProperties";
42 const char kTokenSignatureProperty[] = "SignatureProperty";
43
44 // ATTRIBUTE TOKENS
45 const char kTokenAlgorithm[] = "Algorithm";
46 const char kTokenURI[] = "URI";
47 const char kTokenID[] = "Id";
48
49 // ATTRIBUTE VALUES
50 const char kTokenAttrProfile[] = "profile";
51 const char kTokenAttrRole[] = "role";
52 const char kTokenAttrIdentifier[] = "identifier";
53
54 bool TagNameEquals(const xmlNodePtr node,
55     const char* expected_name, const xmlNsPtr expected_namespace) {
56   if (node->ns != expected_namespace)
57     return false;
58
59   return 0 == strcmp(expected_name, reinterpret_cast<const char*>(node->name));
60 }
61
62 // Returns child nodes of |root| with name |name|.
63 std::vector<xmlNodePtr> GetChildren(
64     const xmlNodePtr root, const xmlNsPtr xml_namespace, const char* name) {
65   std::vector<xmlNodePtr> result;
66   for (xmlNodePtr child = root->children; child != NULL; child = child->next) {
67     if (!TagNameEquals(child, name, xml_namespace))
68       continue;
69
70     result.push_back(child);
71   }
72   return result;
73 }
74
75 // Returns the first child node of |root| with name |name|.
76 xmlNodePtr GetFirstChild(
77     const xmlNodePtr root, const xmlNsPtr xml_namespace, const char* name) {
78   xmlNodePtr result = NULL;
79   for (xmlNodePtr child = root->children; child != NULL; child = child->next) {
80     if (TagNameEquals(child, name, xml_namespace)) {
81       result = child;
82       break;
83     }
84   }
85   return result;
86 }
87
88 // Returns the value of a named attribute, or the empty string.
89 std::string GetAttribute(
90     const xmlNodePtr node, const char* attribute_name) {
91   const xmlChar* name =
92     reinterpret_cast<const xmlChar*>(attribute_name);
93   for (xmlAttr* attr = node->properties; attr != NULL; attr = attr->next) {
94     if (!xmlStrcmp(attr->name, name) && attr->children &&
95         attr->children->content)
96       return std::string(reinterpret_cast<const char*>(
97             attr->children->content));
98   }
99   return std::string();
100 }
101
102 // Returns a pointer to the xmlNs on |node| with the |expected_href|, or
103 // NULL if there isn't one with that href.
104 xmlNsPtr GetNamespace(const xmlNodePtr node, const char* expected_href) {
105   const xmlChar* href = reinterpret_cast<const xmlChar*>(expected_href);
106   for (xmlNsPtr ns = node->ns; ns != NULL; ns = ns->next) {
107     if (ns->href && !xmlStrcmp(ns->href, href))
108       return ns;
109   }
110   return NULL;
111 }
112
113 }  // namespace
114
115 namespace xwalk {
116 namespace application {
117
118 bool ParseSignedInfoElement(
119     const xmlNodePtr node, const xmlNsPtr signature_ns, SignatureData* data) {
120   xmlNodePtr signed_info_node =
121     GetFirstChild(node, signature_ns, kTokenSignedInfo);
122   if (!signed_info_node) {
123     LOG(ERROR) << "Missing SignedInfo tag.";
124     return false;
125   }
126
127   // Parse <CanonicalizationMethod>
128   xmlNodePtr canonicalization_method_node =
129     GetFirstChild(signed_info_node, signature_ns, kTokenCanonicalizationMethod);
130   if (!canonicalization_method_node) {
131     LOG(ERROR) << "Missing SignedInfo tag.";
132     return false;
133   }
134   std::string canonicalization_method =
135     GetAttribute(canonicalization_method_node, kTokenAlgorithm);
136   data->set_canonicalization_method(canonicalization_method);
137
138   // Parse <SignatureMethod>
139   xmlNodePtr signature_method_node =
140     GetFirstChild(signed_info_node, signature_ns, kTokenSignatureMethod);
141   if (!signature_method_node) {
142     LOG(ERROR) << "Missing SignatureMethod tag.";
143     return false;
144   }
145   std::string signature_method =
146     GetAttribute(signature_method_node, kTokenAlgorithm);
147   data->set_signature_method(signature_method);
148
149   // Parse <Reference>
150   std::vector<xmlNodePtr> reference_vec =
151     GetChildren(signed_info_node, signature_ns, kTokenReference);
152   if (reference_vec.empty()) {
153     LOG(ERROR) << "Missing Reference tag.";
154     return false;
155   }
156
157   std::string uri;
158   xmlNodePtr refer_node;
159   std::set<std::string> reference_set;
160   for (size_t i = 0; i < reference_vec.size(); ++i) {
161     refer_node = reference_vec[i];
162     uri = GetAttribute(refer_node, kTokenURI);
163     if (uri.empty()) {
164       LOG(ERROR) << "Missing URI attribute.";
165       return false;
166     }
167     url::RawCanonOutputW<1024> decoded_uri;
168     url::DecodeURLEscapeSequences(uri.c_str(),
169                                   uri.length(),
170                                   &decoded_uri);
171     base::string16 uri16(decoded_uri.data(),
172                          decoded_uri.length());
173     std::string uri8 = base::UTF16ToUTF8(uri16);
174     if (!uri8.empty()) {
175       uri = uri8;
176     } else {
177       LOG(WARNING) << "Failing to decode URI.";
178     }
179     reference_set.insert(uri);
180   }
181   data->set_reference_set(reference_set);
182
183   return true;
184 }
185
186 bool ParseSignatureValueElement(
187     const xmlNodePtr node, const xmlNsPtr ns, SignatureData* data) {
188   xmlNodePtr sign_value_node = GetFirstChild(node, ns, kTokenSignatureValue);
189   if (!sign_value_node) {
190     LOG(ERROR) << "Missing SignatureValue tag.";
191     return false;
192   }
193   std::string signature_value = XmlStringToStdString(
194       xmlNodeGetContent(sign_value_node));
195   data->set_signature_value(signature_value);
196   return true;
197 }
198
199 bool ParseKeyInfoElement(
200     const xmlNodePtr node, const xmlNsPtr ns, SignatureData* data) {
201   xmlNodePtr key_info_node = GetFirstChild(node, ns, kTokenkeyInfo);
202   if (!key_info_node) {
203     LOG(INFO) << "Missing KeyInfo tag, it is allowed by schema.xsd.";
204     return true;
205   }
206
207   // KeyInfo may contain keys, names, certificates and other public key
208   // management. Now I only handle X509 certifcates which is commonly used.
209   // TODO(Xu): Other types of element
210   xmlNodePtr X509_data_node =
211     GetFirstChild(key_info_node, ns, kTokenX509Data);
212   if (!X509_data_node) {
213     LOG(INFO) << "Missing X509Data tag.";
214     return true;
215   }
216
217   // Parse <X509Certificate>
218   std::vector<xmlNodePtr> cert_vec =
219     GetChildren(X509_data_node, ns, kTokenX509Certificate);
220   if (cert_vec.empty()) {
221     LOG(ERROR) << "Missing X509Certificate tag.";
222     return false;
223   }
224
225   std::list<std::string> certificate_list;
226   for (std::vector<xmlNode*>::iterator it = cert_vec.begin();
227       it != cert_vec.end(); ++it) {
228     xmlNodePtr certificate_node = *it;
229     certificate_list.push_back(
230         XmlStringToStdString(xmlNodeGetContent(certificate_node)));
231   }
232   data->set_certificate_list(certificate_list);
233   return true;
234 }
235
236 bool ParseObjectElement(
237     const xmlNodePtr node, const xmlNsPtr ns, SignatureData* data) {
238   xmlNodePtr object_node = GetFirstChild(node, ns, kTokenObject);
239   if (!object_node) {
240     LOG(ERROR) << "Missing Object tag.";
241     return false;
242   }
243
244   std::string object_id = GetAttribute(object_node, kTokenID);
245   data->set_object_id(object_id);
246   // Parse <SignatureProperties>
247   xmlNodePtr properties_node =
248     GetFirstChild(object_node, ns, kTokenSignatureProperties);
249   if (!properties_node) {
250     LOG(ERROR) << "Missing Object tag.";
251     return false;
252   }
253
254   std::vector<xmlNodePtr> prop_vec =
255     GetChildren(properties_node, ns, kTokenSignatureProperty);
256   std::string Id, uri, element_name, profile_uri, role_uri;
257   xmlNodePtr sign_property_node, child;
258   for (size_t i = 0; i < prop_vec.size(); i++) {
259     sign_property_node = prop_vec[i];
260     Id = GetAttribute(sign_property_node, kTokenID);
261     child = sign_property_node->children;
262     if (!child) {
263       LOG(ERROR) << "Failing to find " << element_name
264         << "  element.";
265       return false;
266     }
267
268     if (Id.compare(kTokenAttrProfile) == 0) {
269       profile_uri = GetAttribute(child, kTokenURI);
270       data->set_profile_uri(profile_uri);
271     }
272     if (Id.compare(kTokenAttrRole) == 0) {
273       role_uri = GetAttribute(child, kTokenURI);
274       data->set_role_uri(role_uri);
275     }
276   }
277
278   return true;
279 }
280
281 bool ParseXML(xmlDocPtr docPtr, SignatureData* data) {
282   xmlNodePtr root = xmlDocGetRootElement(docPtr);
283   if (!root) {
284     LOG(ERROR) << "Missinging root node.";
285     return false;
286   }
287
288   // Look for the required namespace declaration.
289   xmlNsPtr signature_ns = GetNamespace(root, kExpectedXmlns);
290   if (!signature_ns) {
291     LOG(ERROR) << "Missinging or incorrect xmlns on signature tag.";
292     return false;
293   }
294   if (!TagNameEquals(root, kTokenSignature, signature_ns)) {
295     LOG(ERROR) << "Missinging Signature tag.";
296     return false;
297   }
298
299   if (!ParseSignedInfoElement(root, signature_ns, data))
300     return false;
301
302   if (!ParseSignatureValueElement(root, signature_ns, data))
303     return false;
304
305   if (!ParseKeyInfoElement(root, signature_ns, data))
306     return false;
307
308   if (!ParseObjectElement(root, signature_ns, data))
309     return false;
310
311   return true;
312 }
313
314 // static
315 scoped_ptr<SignatureData> SignatureParser::CreateSignatureData(
316     const base::FilePath& signature_path, int signature_number) {
317   std::string file_name = signature_path.MaybeAsASCII();
318   scoped_ptr<SignatureData>
319     data(new SignatureData(file_name, signature_number));
320
321   xmlInitParser();
322   xmlDocPtr doc = xmlParseFile(file_name.c_str());
323   if (!doc) {
324     LOG(ERROR) << "Opening signature " << file_name << " failed.";
325     return scoped_ptr<SignatureData>();
326   }
327
328   if (!ParseXML(doc, data.get())) {
329     LOG(ERROR) << "Parsering failed.";
330     xmlFreeDoc(doc);
331     return scoped_ptr<SignatureData>();
332   }
333
334   xmlFreeDoc(doc);
335   xmlCleanupParser();
336   return data.Pass();
337 }
338 }  // namespace application
339 }  // namespace xwalk