This is digital signature implementation moved from xwalk.
It is based on following PRs:
- https://github.com/crosswalk-project/crosswalk/pull/2169
- https://github.com/crosswalk-project/crosswalk/pull/2291
- https://github.com/crosswalk-project/crosswalk/pull/2422
There are some not solved issues:
- test correctness for valid widgets,
- seperation from managing root certificate,
- style of this code.
[[ PLEASE NOTE THE COMMIT CONTAINS CROSSWALK'S BSD-STYLE LICENSE ]]
Change-Id: I42db7e8a02b44a88ca88d143ad034fd1e253f3e2
Signed-off-by: Pawel Sikorski <p.sikorski@samsung.com>
# Targets
SET(TARGET_LIBNAME_COMMON "common-installer")
+SET(TARGET_LIBNAME_SIGNATURE "common-installer-signature")
SET(TARGET_WGT_BACKEND "wgt-backend")
SET(TARGET_XPK_BACKEND "xpk-backend")
SET(TARGET_NATIVE_BACKEND "native-backend")
PKG_CHECK_MODULES(PKGMGR_INSTALLER_DEPS REQUIRED pkgmgr-installer)
PKG_CHECK_MODULES(MINIZIP_DEPS REQUIRED minizip)
PKG_CHECK_MODULES(ZLIB_DEPS REQUIRED zlib)
+PKG_CHECK_MODULES(OPENSSL_DEPS REQUIRED openssl)
PKG_CHECK_MODULES(LIBXML_DEPS REQUIRED libxml-2.0)
+PKG_CHECK_MODULES(XMLSEC1_DEPS REQUIRED xmlsec1)
-FIND_PACKAGE(Boost COMPONENTS filesystem system REQUIRED)
+FIND_PACKAGE(Boost REQUIRED COMPONENTS system filesystem)
+
+# xmlsec1 - choose crypto library which to use
+ADD_DEFINITIONS("-DXMLSEC_CRYPTO_OPENSSL")
ADD_SUBDIRECTORY(common)
+ADD_SUBDIRECTORY(signature)
ADD_SUBDIRECTORY(wgt)
#ADD_SUBDIRECTORY(xpk)
#ADD_SUBDIRECTORY(native)
+ADD_SUBDIRECTORY(data)
--- /dev/null
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Intel Corporation nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
SET(SRCS
src/app_installer.cc
src/step/step_unzip.cc
+ src/step/signature_step.cc
)
# Target - definition
ADD_LIBRARY(${TARGET_LIBNAME_COMMON} SHARED ${SRCS})
LIBXML_DEPS
Boost
)
+# Target in-package deps
+TARGET_LINK_LIBRARIES(${TARGET_LIBNAME_COMMON} ${TARGET_LIBNAME_SIGNATURE})
# Extra
SET_TARGET_PROPERTIES(${TARGET_LIBNAME_COMMON} PROPERTIES VERSION ${VERSION})
--- /dev/null
+// Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by an apache 2.0 license that can be
+// found in the LICENSE file.
+
+#ifndef COMMON_STEP_SIGNATURE_SIGNATURE_STEP_H_
+#define COMMON_STEP_SIGNATURE_SIGNATURE_STEP_H_
+
+#include "context_installer.h"
+#include "step.h"
+
+namespace common {
+
+class SignatureStep : public Step {
+ public:
+ using Step::Step;
+
+ int process(Context_installer* context) override;
+ int undo(Context_installer*) override { return 0; }
+ int clean(Context_installer*) override { return 0; }
+};
+
+} // namespace common
+
+#endif // COMMON_STEP_SIGNATURE_SIGNATURE_STEP_H_
--- /dev/null
+// Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by an apache 2.0 license that can be
+// found in the LICENSE file.
+
+#include <step/signature_step.h>
+
+#include <boost/filesystem/path.hpp>
+
+#include <string>
+
+#include <signature_validator.h>
+
+namespace bf = boost::filesystem;
+
+namespace common {
+
+int SignatureStep::process(Context_installer* context) {
+ return (signature::SignatureValidator::Check(bf::path(context->unpack_directory))
+ == signature::SignatureValidator::INVALID) ? -1 : 0;
+}
+
+} // namespace common
--- /dev/null
+INSTALL(FILES "signature_schema.xsd" DESTINATION "share/app-installers/")
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE schema
+ PUBLIC "-//W3C//DTD XMLSchema 200102//EN" "http://www.w3.org/2001/XMLSchema.dtd"
+ [
+ <!ATTLIST schema
+ xmlns:ds CDATA #FIXED "http://www.w3.org/2000/09/xmldsig#">
+ <!ENTITY dsig 'http://www.w3.org/2000/09/xmldsig#'>
+ <!ENTITY % p ''>
+ <!ENTITY % s ''>
+ ]>
+
+<!-- Schema for XML Signatures
+ http://www.w3.org/2000/09/xmldsig#
+ $Revision: 1.1 $ on $Date: 2002/02/08 20:32:26 $ by $Author: reagle $
+
+ Copyright 2001 The Internet Society and W3C (Massachusetts Institute
+ of Technology, Institut National de Recherche en Informatique et en
+ Automatique, Keio University). All Rights Reserved.
+ http://www.w3.org/Consortium/Legal/
+
+ This document is governed by the W3C Software License [1] as described
+ in the FAQ [2].
+
+ [1] http://www.w3.org/Consortium/Legal/copyright-software-19980720
+ [2] http://www.w3.org/Consortium/Legal/IPR-FAQ-20000620.html#DTD
+-->
+
+
+<schema xmlns="http://www.w3.org/2001/XMLSchema"
+ xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+ targetNamespace="http://www.w3.org/2000/09/xmldsig#"
+ version="0.1" elementFormDefault="qualified">
+
+<!-- Basic Types Defined for Signatures -->
+
+<simpleType name="CryptoBinary">
+ <restriction base="base64Binary">
+ </restriction>
+</simpleType>
+
+<!-- Start Signature -->
+
+<element name="Signature" type="ds:SignatureType"/>
+<complexType name="SignatureType">
+ <sequence>
+ <element ref="ds:SignedInfo"/>
+ <element ref="ds:SignatureValue"/>
+ <element ref="ds:KeyInfo" minOccurs="0"/>
+ <element ref="ds:Object" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="Id" type="ID" use="optional"/>
+</complexType>
+
+ <element name="SignatureValue" type="ds:SignatureValueType"/>
+ <complexType name="SignatureValueType">
+ <simpleContent>
+ <extension base="base64Binary">
+ <attribute name="Id" type="ID" use="optional"/>
+ </extension>
+ </simpleContent>
+ </complexType>
+
+<!-- Start SignedInfo -->
+
+<element name="SignedInfo" type="ds:SignedInfoType"/>
+<complexType name="SignedInfoType">
+ <sequence>
+ <element ref="ds:CanonicalizationMethod"/>
+ <element ref="ds:SignatureMethod"/>
+ <element ref="ds:Reference" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="Id" type="ID" use="optional"/>
+</complexType>
+
+ <element name="CanonicalizationMethod" type="ds:CanonicalizationMethodType"/>
+ <complexType name="CanonicalizationMethodType" mixed="true">
+ <sequence>
+ <any namespace="##any" minOccurs="0" maxOccurs="unbounded"/>
+ <!-- (0,unbounded) elements from (1,1) namespace -->
+ </sequence>
+ <attribute name="Algorithm" type="anyURI" use="required"/>
+ </complexType>
+
+ <element name="SignatureMethod" type="ds:SignatureMethodType"/>
+ <complexType name="SignatureMethodType" mixed="true">
+ <sequence>
+ <element name="HMACOutputLength" minOccurs="0" type="ds:HMACOutputLengthType"/>
+ <any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
+ <!-- (0,unbounded) elements from (1,1) external namespace -->
+ </sequence>
+ <attribute name="Algorithm" type="anyURI" use="required"/>
+ </complexType>
+
+<!-- Start Reference -->
+
+<element name="Reference" type="ds:ReferenceType"/>
+<complexType name="ReferenceType">
+ <sequence>
+ <element ref="ds:Transforms" minOccurs="0"/>
+ <element ref="ds:DigestMethod"/>
+ <element ref="ds:DigestValue"/>
+ </sequence>
+ <attribute name="Id" type="ID" use="optional"/>
+ <attribute name="URI" type="anyURI" use="optional"/>
+ <attribute name="Type" type="anyURI" use="optional"/>
+</complexType>
+
+ <element name="Transforms" type="ds:TransformsType"/>
+ <complexType name="TransformsType">
+ <sequence>
+ <element ref="ds:Transform" maxOccurs="unbounded"/>
+ </sequence>
+ </complexType>
+
+ <element name="Transform" type="ds:TransformType"/>
+ <complexType name="TransformType" mixed="true">
+ <choice minOccurs="0" maxOccurs="unbounded">
+ <any namespace="##other" processContents="lax"/>
+ <!-- (1,1) elements from (0,unbounded) namespaces -->
+ <element name="XPath" type="string"/>
+ </choice>
+ <attribute name="Algorithm" type="anyURI" use="required"/>
+ </complexType>
+
+<!-- End Reference -->
+
+<element name="DigestMethod" type="ds:DigestMethodType"/>
+<complexType name="DigestMethodType" mixed="true">
+ <sequence>
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="Algorithm" type="anyURI" use="required"/>
+</complexType>
+
+<element name="DigestValue" type="ds:DigestValueType"/>
+<simpleType name="DigestValueType">
+ <restriction base="base64Binary"/>
+</simpleType>
+
+<!-- End SignedInfo -->
+
+<!-- Start KeyInfo -->
+
+<element name="KeyInfo" type="ds:KeyInfoType"/>
+<complexType name="KeyInfoType" mixed="true">
+ <choice maxOccurs="unbounded">
+ <element ref="ds:KeyName"/>
+ <element ref="ds:KeyValue"/>
+ <element ref="ds:RetrievalMethod"/>
+ <element ref="ds:X509Data"/>
+ <element ref="ds:PGPData"/>
+ <element ref="ds:SPKIData"/>
+ <element ref="ds:MgmtData"/>
+ <any processContents="lax" namespace="##other"/>
+ <!-- (1,1) elements from (0,unbounded) namespaces -->
+ </choice>
+ <attribute name="Id" type="ID" use="optional"/>
+</complexType>
+
+ <element name="KeyName" type="string"/>
+ <element name="MgmtData" type="string"/>
+
+ <element name="KeyValue" type="ds:KeyValueType"/>
+ <complexType name="KeyValueType" mixed="true">
+ <choice>
+ <element ref="ds:DSAKeyValue"/>
+ <element ref="ds:RSAKeyValue"/>
+ <element ref="ds:ECKeyValue"/>
+ <any namespace="##other" processContents="lax"/>
+ </choice>
+ </complexType>
+
+<!-- ECDSA KEY DEFINITIONS -->
+
+ <element name="ECKeyValue" type="ds:ECKeyValueType"/>
+ <complexType name="ECKeyValueType">
+ <sequence>
+ <choice>
+ <element name="ECParameters" type="ds:ECParametersType"/>
+ <element name="NamedCurve" type="ds:NamedCurveType"/>
+ </choice>
+ <element name="PublicKey" type="ds:ECPointType"/>
+ </sequence>
+ <attribute name="Id" type="ID" use="optional"/>
+ </complexType>
+
+ <complexType name="NamedCurveType">
+ <attribute name="URI" type="anyURI" use="required"/>
+ </complexType>
+
+ <simpleType name="ECPointType">
+ <restriction base="ds:CryptoBinary"/>
+ </simpleType>
+
+ <element name="RetrievalMethod" type="ds:RetrievalMethodType"/>
+ <complexType name="RetrievalMethodType">
+ <sequence>
+ <element ref="ds:Transforms" minOccurs="0"/>
+ </sequence>
+ <attribute name="URI" type="anyURI"/>
+ <attribute name="Type" type="anyURI" use="optional"/>
+ </complexType>
+
+ <complexType name="ECParametersType">
+ <sequence>
+ <element name="FieldID" type="ds:FieldIDType"/>
+ <element name="Curve" type="ds:CurveType"/>
+ <element name="Base" type="ds:ECPointType"/>
+ <element name="Order" type="ds:CryptoBinary"/>
+ <element name="CoFactor" type="integer" minOccurs="0"/>
+ <element name="ValidationData" type="ds:ECValidationDataType" minOccurs="0"/>
+ </sequence>
+ </complexType>
+
+ <complexType name="FieldIDType">
+ <choice>
+ <element ref="ds:Prime"/>
+ <element ref="ds:TnB"/>
+ <element ref="ds:PnB"/>
+ <element ref="ds:GnB"/>
+ <any namespace="##other" processContents="lax"/>
+ </choice>
+ </complexType>
+
+ <element name="Prime" type="ds:PrimeFieldParamsType"/>
+ <complexType name="PrimeFieldParamsType">
+ <sequence>
+ <element name="P" type="ds:CryptoBinary"/>
+ </sequence>
+ </complexType>
+
+ <element name="GnB" type="ds:CharTwoFieldParamsType"/>
+ <complexType name="CharTwoFieldParamsType">
+ <sequence>
+ <element name="M" type="positiveInteger"/>
+ </sequence>
+ </complexType>
+
+ <element name="TnB" type="ds:TnBFieldParamsType"/>
+ <complexType name="TnBFieldParamsType">
+ <complexContent>
+ <extension base="ds:CharTwoFieldParamsType">
+ <sequence>
+ <element name="K" type="positiveInteger"/>
+ </sequence>
+ </extension>
+ </complexContent>
+ </complexType>
+
+ <element name="PnB" type="ds:PnBFieldParamsType"/>
+ <complexType name="PnBFieldParamsType">
+ <complexContent>
+ <extension base="ds:CharTwoFieldParamsType">
+ <sequence>
+ <element name="K1" type="positiveInteger"/>
+ <element name="K2" type="positiveInteger"/>
+ <element name="K3" type="positiveInteger"/>
+ </sequence>
+ </extension>
+ </complexContent>
+ </complexType>
+
+ <complexType name="CurveType">
+ <sequence>
+ <element name="A" type="ds:CryptoBinary"/>
+ <element name="B" type="ds:CryptoBinary"/>
+ </sequence>
+ </complexType>
+
+ <complexType name="ECValidationDataType">
+ <sequence>
+ <element name="seed" type="ds:CryptoBinary"/>
+ </sequence>
+ <attribute name="hashAlgorithm" type="anyURI" use="required"/>
+ </complexType>
+
+
+<!-- Start X509Data -->
+
+<element name="X509Data" type="ds:X509DataType"/>
+<complexType name="X509DataType">
+ <sequence maxOccurs="unbounded">
+ <choice>
+ <element name="X509IssuerSerial" type="ds:X509IssuerSerialType"/>
+ <element name="X509SKI" type="base64Binary"/>
+ <element name="X509SubjectName" type="string"/>
+ <element name="X509Certificate" type="base64Binary"/>
+ <element name="X509CRL" type="base64Binary"/>
+ <any namespace="##other" processContents="lax"/>
+ </choice>
+ </sequence>
+</complexType>
+
+<complexType name="X509IssuerSerialType">
+ <sequence>
+ <element name="X509IssuerName" type="string"/>
+ <element name="X509SerialNumber" type="integer"/>
+ </sequence>
+</complexType>
+
+<!-- End X509Data -->
+
+<!-- Begin PGPData -->
+
+<element name="PGPData" type="ds:PGPDataType"/>
+<complexType name="PGPDataType">
+ <choice>
+ <sequence>
+ <element name="PGPKeyID" type="base64Binary"/>
+ <element name="PGPKeyPacket" type="base64Binary" minOccurs="0"/>
+ <any namespace="##other" processContents="lax" minOccurs="0"
+ maxOccurs="unbounded"/>
+ </sequence>
+ <sequence>
+ <element name="PGPKeyPacket" type="base64Binary"/>
+ <any namespace="##other" processContents="lax" minOccurs="0"
+ maxOccurs="unbounded"/>
+ </sequence>
+ </choice>
+</complexType>
+
+<!-- End PGPData -->
+
+<!-- Begin SPKIData -->
+
+<element name="SPKIData" type="ds:SPKIDataType"/>
+<complexType name="SPKIDataType">
+ <sequence maxOccurs="unbounded">
+ <element name="SPKISexp" type="base64Binary"/>
+ <any namespace="##other" processContents="lax" minOccurs="0"/>
+ </sequence>
+</complexType>
+
+<!-- End SPKIData -->
+
+<!-- End KeyInfo -->
+
+<!-- Start Object (Manifest, SignatureProperty) -->
+
+<element name="Object" type="ds:ObjectType"/>
+<complexType name="ObjectType" mixed="true">
+ <sequence minOccurs="0" maxOccurs="unbounded">
+ <any namespace="##any" processContents="lax"/>
+ </sequence>
+ <attribute name="Id" type="ID" use="optional"/>
+ <attribute name="MimeType" type="string" use="optional"/> <!-- add a grep facet -->
+ <attribute name="Encoding" type="anyURI" use="optional"/>
+</complexType>
+
+<element name="Manifest" type="ds:ManifestType"/>
+<complexType name="ManifestType">
+ <sequence>
+ <element ref="ds:Reference" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="Id" type="ID" use="optional"/>
+</complexType>
+
+<element name="SignatureProperties" type="ds:SignaturePropertiesType"/>
+<complexType name="SignaturePropertiesType">
+ <sequence>
+ <element ref="ds:SignatureProperty" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="Id" type="ID" use="optional"/>
+</complexType>
+
+ <element name="SignatureProperty" type="ds:SignaturePropertyType"/>
+ <complexType name="SignaturePropertyType" mixed="true">
+ <choice maxOccurs="unbounded">
+ <any namespace="##other" processContents="lax"/>
+ <!-- (1,1) elements from (1,unbounded) namespaces -->
+ </choice>
+ <attribute name="Target" type="anyURI" use="required"/>
+ <attribute name="Id" type="ID" use="optional"/>
+ </complexType>
+
+<!-- End Object (Manifest, SignatureProperty) -->
+
+<!-- Start Algorithm Parameters -->
+
+<simpleType name="HMACOutputLengthType">
+ <restriction base="integer"/>
+</simpleType>
+
+<!-- Start KeyValue Element-types -->
+
+<element name="DSAKeyValue" type="ds:DSAKeyValueType"/>
+<complexType name="DSAKeyValueType">
+ <sequence>
+ <sequence minOccurs="0">
+ <element name="P" type="ds:CryptoBinary"/>
+ <element name="Q" type="ds:CryptoBinary"/>
+ </sequence>
+ <element name="G" type="ds:CryptoBinary" minOccurs="0"/>
+ <element name="Y" type="ds:CryptoBinary"/>
+ <element name="J" type="ds:CryptoBinary" minOccurs="0"/>
+ <sequence minOccurs="0">
+ <element name="Seed" type="ds:CryptoBinary"/>
+ <element name="PgenCounter" type="ds:CryptoBinary"/>
+ </sequence>
+ </sequence>
+</complexType>
+
+<element name="RSAKeyValue" type="ds:RSAKeyValueType"/>
+<complexType name="RSAKeyValueType">
+ <sequence>
+ <element name="Modulus" type="ds:CryptoBinary"/>
+ <element name="Exponent" type="ds:CryptoBinary"/>
+ </sequence>
+</complexType>
+
+<!-- End KeyValue Element-types -->
+
+<!-- End Signature -->
+
+</schema>
License: Apache-2.0
Source0: %{name}-%{version}.tar.gz
-BuildRequires: libcap-devel
+BuildRequires: boost-devel
BuildRequires: cmake
+BuildRequires: libcap-devel
BuildRequires: pkgconfig(pkgmgr)
BuildRequires: pkgconfig(pkgmgr-parser)
BuildRequires: pkgconfig(pkgmgr-info)
BuildRequires: pkgconfig(pkgmgr-installer)
+BuildRequires: pkgconfig(openssl)
BuildRequires: pkgconfig(libxml-2.0)
BuildRequires: pkgconfig(zlib)
BuildRequires: pkgconfig(minizip)
-BuildRequires: boost-devel
+BuildRequires: pkgconfig(libzip)
+BuildRequires: pkgconfig(xmlsec1)
+
+Requires: ca-certificates-tizen
%description
This is a meta package that installs the common application
%files
%defattr(-,root,root)
%{_libdir}/libcommon-installer.so*
-%license LICENSE
+%{_libdir}/libcommon-installer-signature.so*
+%{_datarootdir}/app-installers/signature_schema.xsd
+%license LICENSE LICENSE-xwalk
%files -n wgt-backend
%{_sysconfdir}/package-manager/backend/wgt
--- /dev/null
+# Target - sources
+SET(SRCS
+ src/signature_data.cc
+ src/signature_parser.cc
+ src/signature_validator.cc
+ src/signature_xmlsec_adaptor.cc
+)
+# Target - definition
+ADD_LIBRARY(${TARGET_LIBNAME_SIGNATURE} SHARED ${SRCS})
+# Target - includes
+TARGET_INCLUDE_DIRECTORIES(${TARGET_LIBNAME_SIGNATURE} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
+# Target - deps
+APPLY_PKG_CONFIG(${TARGET_LIBNAME_SIGNATURE} PUBLIC
+ OPENSSL_DEPS
+ XMLSEC1_DEPS
+ Boost
+)
+
+# Extra
+SET_TARGET_PROPERTIES(${TARGET_LIBNAME_SIGNATURE} PROPERTIES VERSION ${VERSION})
+SET_TARGET_PROPERTIES(${TARGET_LIBNAME_SIGNATURE} PROPERTIES SOVERSION ${VERSION_MAJOR})
+
+# Install
+INSTALL(TARGETS ${TARGET_LIBNAME_SIGNATURE} DESTINATION ${LIB_INSTALL_DIR})
--- /dev/null
+// Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by a apache 2.0 license that can be
+// found in the LICENSE file.
+
+#ifndef UTILS_LOGGING_H_
+#define UTILS_LOGGING_H_
+
+#include <iostream>
+#include <sstream>
+
+// TODO(tiwanek): Logging...
+
+namespace detail {
+
+class LogCatcher {
+ public:
+ LogCatcher() { }
+ void operator&(const std::ostream& str) const {
+ // TODO(tiwanek): this cast is error-prone - fix it
+ std::cerr << static_cast<const std::ostringstream*>(&str)->str()
+ << std::endl;
+ }
+};
+
+}
+
+#define LOG(LEVEL) \
+ ::detail::LogCatcher() & std::ostringstream() << "[" << #LEVEL << "] " \
+
+#endif // UTILS_LOGGING_H_
--- /dev/null
+// Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by a apache 2.0 license that can be
+// found in the LICENSE file.
+
+#ifndef UTILS_MARCOS_H_
+#define UTILS_MARCOS_H_
+
+#define DISALLOW_COPY_AND_ASSIGN(CLASS) \
+ CLASS(const CLASS&) = delete; \
+ CLASS& operator=(const CLASS&) = delete \
+
+#endif // UTILS_MARCOS_H_
--- /dev/null
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by an apache 2.0 license that can be
+// found in the LICENSE file.
+// Part of this file is redistributed from crosswalk project under BDS-style
+// license. Check LICENSE-xwalk file.
+
+#ifndef SIGNATURE_SIGNATURE_DATA_H_
+#define SIGNATURE_SIGNATURE_DATA_H_
+
+#include <boost/filesystem/path.hpp>
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+
+#include <list>
+#include <set>
+#include <string>
+
+#include <marcos.h>
+
+namespace signature {
+
+class SignatureData {
+ public:
+ SignatureData(const boost::filesystem::path& signature_file_name,
+ int signature_number);
+ ~SignatureData();
+
+ boost::filesystem::path signature_file_name() const {
+ return signature_file_name_;
+ }
+
+ const std::set<std::string>& reference_set() const {
+ return reference_set_;
+ }
+
+ void set_reference_set(const std::set<std::string>& reference_set) {
+ reference_set_ = reference_set;
+ }
+
+ std::string role_uri() const {
+ return role_uri_;
+ }
+
+ void set_role_uri(const std::string& role_uri) {
+ role_uri_ = role_uri;
+ }
+
+ std::string profile_uri() const {
+ return profile_uri_;
+ }
+
+ void set_profile_uri(const std::string& profile_uri) {
+ profile_uri_ = profile_uri;
+ }
+
+ std::string object_id() const {
+ return object_id_;
+ }
+
+ void set_object_id(const std::string& object_id) {
+ object_id_ = object_id;
+ }
+
+ std::string signature_value() const {
+ return signature_value_;
+ }
+
+ void set_signature_value(const std::string& signature_value) {
+ signature_value_ = signature_value;
+ }
+
+ std::string canonicalization_method() const {
+ return canonicalization_method_;
+ }
+
+ void set_canonicalization_method(const std::string& canonicalization_method) {
+ canonicalization_method_ = canonicalization_method;
+ }
+
+ std::string signature_method() const {
+ return signature_method_;
+ }
+
+ void set_signature_method(const std::string& signature_method) {
+ signature_method_ = signature_method;
+ }
+
+ const std::list<std::string>& certificate_list() const {
+ return certificate_list_;
+ }
+
+ void set_certificate_list(const std::list<std::string>& certificate_list) {
+ certificate_list_ = certificate_list;
+ }
+
+ bool isAuthorSignature() const {
+ return signature_number_ == -1;
+ }
+
+ boost::filesystem::path GetExtractedWidgetPath() const;
+
+ private:
+ boost::filesystem::path signature_file_name_;
+ // This number is taken from distributor signature file name.
+ // Author signature do not contain any number on the file name.
+ // Author signature should have signature number equal to -1.
+ int signature_number_;
+ std::string role_uri_;
+ std::string profile_uri_;
+ std::string signature_value_;
+ std::string identifier;
+ std::string object_id_;
+ std::string canonicalization_method_;
+ std::string signature_method_;
+ std::set<std::string> reference_set_;
+ std::list<std::string> certificate_list_;
+};
+
+} // namespace signature
+
+
+#endif // SIGNATURE_SIGNATURE_DATA_H_
--- /dev/null
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by an apache 2.0 license that can be
+// found in the LICENSE file.
+// Part of this file is redistributed from crosswalk project under BDS-style
+// license. Check LICENSE-xwalk file.
+
+#ifndef SIGNATURE_SIGNATURE_PARSER_H_
+#define SIGNATURE_SIGNATURE_PARSER_H_
+
+#include <boost/filesystem/path.hpp>
+
+#include <memory>
+
+#include <marcos.h>
+#include <signature_data.h>
+
+namespace signature {
+
+class SignatureParser {
+ public:
+ static std::unique_ptr<SignatureData> CreateSignatureData(
+ const boost::filesystem::path& signature_path, int signature_number);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SignatureParser);
+};
+
+} // namespace signature
+
+#endif // SIGNATURE_SIGNATURE_PARSER_H_
--- /dev/null
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by an apache 2.0 license that can be
+// found in the LICENSE file.
+// Part of this file is redistributed from crosswalk project under BDS-style
+// license. Check LICENSE-xwalk file.
+
+#ifndef COMMON_STEP_SIGNATURE_SIGNATURE_VALIDATOR_H_
+#define COMMON_STEP_SIGNATURE_SIGNATURE_VALIDATOR_H_
+
+#include <boost/filesystem.hpp>
+
+#include <string>
+
+#include <marcos.h>
+
+namespace signature {
+
+class SignatureValidator {
+ public:
+ enum Status {
+ UNTRUSTED,
+ VALID,
+ INVALID
+ };
+
+ static Status Check(const boost::filesystem::path& widget_path);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SignatureValidator);
+};
+
+} // namespace signature
+
+#endif // COMMON_STEP_SIGNATURE_SIGNATURE_VALIDATOR_H_
--- /dev/null
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by an apache 2.0 license that can be
+// found in the LICENSE file.
+// Part of this file is redistributed from crosswalk project under BDS-style
+// license. Check LICENSE-xwalk file.
+
+#ifndef SIGNATURE_SIGNATURE_XMLSEC_ADAPTOR_H_
+#define SIGNATURE_SIGNATURE_XMLSEC_ADAPTOR_H_
+
+#include <boost/filesystem/path.hpp>
+
+#include <signature_data.h>
+
+namespace signature {
+
+class SignatureXmlSecAdaptor {
+ public:
+ static bool ValidateFile(const SignatureData& signature_data);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SignatureXmlSecAdaptor);
+};
+
+} // namespace signature
+
+#endif // COMMON_STEP_SIGNATURE_SIGNATURE_XMLSEC_ADAPTOR_H_
--- /dev/null
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by an apache 2.0 license that can be
+// found in the LICENSE file.
+// Part of this file is redistributed from crosswalk project under BDS-style
+// license. Check LICENSE-xwalk file.
+
+#include <signature_data.h>
+
+namespace signature {
+
+SignatureData::SignatureData(const boost::filesystem::path& signature_file_name,
+ int signature_number)
+ : signature_file_name_(signature_file_name),
+ signature_number_(signature_number) {
+}
+
+SignatureData::~SignatureData() {
+}
+
+boost::filesystem::path SignatureData::GetExtractedWidgetPath() const {
+ return signature_file_name().parent_path();
+}
+
+} // namespace signature
--- /dev/null
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright (c) 2014 Intel Corporation.
+// Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by an apache 2.0 license that can be
+// found in the LICENSE file.
+// Part of this file is redistributed from crosswalk project under BDS-style
+// license. Check LICENSE-xwalk file.
+
+#include <signature_parser.h>
+
+#include <libxml/parser.h>
+#include <libxml/xmlschemas.h>
+#include <libxml/xpathInternals.h>
+#include <libxml/xmlreader.h>
+
+#include <cstring>
+#include <list>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <logging.h>
+#include <marcos.h>
+
+namespace {
+const char kExpectedXmlns[] = "http://www.w3.org/2000/09/xmldsig#";
+// TAG TOKENS
+const char kTokenSignature[] = "Signature";
+const char kTokenSignedInfo[] = "SignedInfo";
+const char kTokenCanonicalizationMethod[] = "CanonicalizationMethod";
+const char kTokenSignatureMethod[] = "SignatureMethod";
+const char kTokenReference[] = "Reference";
+const char kTokenTransforms[] = "Transforms";
+const char kTokenTransform[] = "Transform";
+const char kTokenDigestMethod[] = "DigestMethod";
+const char kTokenDigestValue[] = "DigestValue";
+const char kTokenSignatureValue[] = "SignatureValue";
+const char kTokenkeyInfo[] = "KeyInfo";
+const char kTokenX509Data[] = "X509Data";
+const char kTokenX509Certificate[] = "X509Certificate";
+const char kTokenObject[] = "Object";
+const char kTokenSignatureProperties[] = "SignatureProperties";
+const char kTokenSignatureProperty[] = "SignatureProperty";
+
+// ATTRIBUTE TOKENS
+const char kTokenAlgorithm[] = "Algorithm";
+const char kTokenURI[] = "URI";
+const char kTokenID[] = "Id";
+
+// ATTRIBUTE VALUES
+const char kTokenAttrProfile[] = "profile";
+const char kTokenAttrRole[] = "role";
+const char kTokenAttrIdentifier[] = "identifier";
+
+bool TagNameEquals(const xmlNodePtr node,
+ const char* expected_name, const xmlNsPtr expected_namespace) {
+ if (node->ns != expected_namespace)
+ return false;
+
+ return 0 == strcmp(expected_name, reinterpret_cast<const char*>(node->name));
+}
+
+// Returns child nodes of |root| with name |name|.
+std::vector<xmlNodePtr> GetChildren(
+ const xmlNodePtr root, const xmlNsPtr xml_namespace, const char* name) {
+ std::vector<xmlNodePtr> result;
+ for (xmlNodePtr child = root->children; child != nullptr;
+ child = child->next) {
+ if (!TagNameEquals(child, name, xml_namespace))
+ continue;
+
+ result.push_back(child);
+ }
+ return result;
+}
+
+// Returns the first child node of |root| with name |name|.
+xmlNodePtr GetFirstChild(
+ const xmlNodePtr root, const xmlNsPtr xml_namespace, const char* name) {
+ xmlNodePtr result = nullptr;
+ for (xmlNodePtr child = root->children; child != nullptr;
+ child = child->next) {
+ if (TagNameEquals(child, name, xml_namespace)) {
+ result = child;
+ break;
+ }
+ }
+ return result;
+}
+
+// Returns the value of a named attribute, or the empty string.
+std::string GetAttribute(
+ const xmlNodePtr node, const char* attribute_name) {
+ const xmlChar* name =
+ reinterpret_cast<const xmlChar*>(attribute_name);
+ for (xmlAttr* attr = node->properties; attr != nullptr; attr = attr->next) {
+ if (!xmlStrcmp(attr->name, name) && attr->children &&
+ attr->children->content)
+ return std::string(reinterpret_cast<const char*>(
+ attr->children->content));
+ }
+ return std::string();
+}
+
+// Returns a pointer to the xmlNs on |node| with the |expected_href|, or
+// nullptr if there isn't one with that href.
+xmlNsPtr GetNamespace(const xmlNodePtr node, const char* expected_href) {
+ const xmlChar* href = reinterpret_cast<const xmlChar*>(expected_href);
+ for (xmlNsPtr ns = node->ns; ns != nullptr; ns = ns->next) {
+ if (ns->href && !xmlStrcmp(ns->href, href))
+ return ns;
+ }
+ return nullptr;
+}
+
+std::string XmlStringToStdString(const xmlChar* xmlstring) {
+ if (xmlstring)
+ return std::string(reinterpret_cast<const char*>(xmlstring));
+ else
+ return "";
+}
+
+} // namespace
+
+namespace signature {
+
+bool ParseSignedInfoElement(
+ const xmlNodePtr node, const xmlNsPtr signature_ns, SignatureData* data) {
+ xmlNodePtr signed_info_node =
+ GetFirstChild(node, signature_ns, kTokenSignedInfo);
+ if (!signed_info_node) {
+ LOG(ERROR) << "Missing SignedInfo tag.";
+ return false;
+ }
+
+ // Parse <CanonicalizationMethod>
+ xmlNodePtr canonicalization_method_node =
+ GetFirstChild(signed_info_node, signature_ns, kTokenCanonicalizationMethod);
+ if (!canonicalization_method_node) {
+ LOG(ERROR) << "Missing SignedInfo tag.";
+ return false;
+ }
+ std::string canonicalization_method =
+ GetAttribute(canonicalization_method_node, kTokenAlgorithm);
+ data->set_canonicalization_method(canonicalization_method);
+
+ // Parse <SignatureMethod>
+ xmlNodePtr signature_method_node =
+ GetFirstChild(signed_info_node, signature_ns, kTokenSignatureMethod);
+ if (!signature_method_node) {
+ LOG(ERROR) << "Missing SignatureMethod tag.";
+ return false;
+ }
+ std::string signature_method =
+ GetAttribute(signature_method_node, kTokenAlgorithm);
+ data->set_signature_method(signature_method);
+
+ // Parse <Reference>
+ std::vector<xmlNodePtr> reference_vec =
+ GetChildren(signed_info_node, signature_ns, kTokenReference);
+ if (reference_vec.empty()) {
+ LOG(ERROR) << "Missing Reference tag.";
+ return false;
+ }
+
+ std::string uri;
+ xmlNodePtr refer_node;
+ std::set<std::string> reference_set;
+ for (size_t i = 0; i < reference_vec.size(); ++i) {
+ refer_node = reference_vec[i];
+ uri = GetAttribute(refer_node, kTokenURI);
+ if (uri.empty()) {
+ LOG(ERROR) << "Missing URI attribute.";
+ return false;
+ }
+ reference_set.insert(uri);
+ }
+ data->set_reference_set(reference_set);
+
+ return true;
+}
+
+bool ParseSignatureValueElement(
+ const xmlNodePtr node, const xmlNsPtr ns, SignatureData* data) {
+ xmlNodePtr sign_value_node = GetFirstChild(node, ns, kTokenSignatureValue);
+ if (!sign_value_node) {
+ LOG(ERROR) << "Missing SignatureValue tag.";
+ return false;
+ }
+ std::string signature_value = XmlStringToStdString(
+ xmlNodeGetContent(sign_value_node));
+ data->set_signature_value(signature_value);
+ return true;
+}
+
+bool ParseKeyInfoElement(
+ const xmlNodePtr node, const xmlNsPtr ns, SignatureData* data) {
+ xmlNodePtr key_info_node = GetFirstChild(node, ns, kTokenkeyInfo);
+ if (!key_info_node) {
+ LOG(INFO) << "Missing KeyInfo tag, it is allowed by schema.xsd.";
+ return true;
+ }
+
+ // KeyInfo may contain keys, names, certificates and other public key
+ // management. Now I only handle X509 certifcates which is commonly used.
+ // TODO(Xu): Other types of element
+ xmlNodePtr X509_data_node =
+ GetFirstChild(key_info_node, ns, kTokenX509Data);
+ if (!X509_data_node) {
+ LOG(INFO) << "Missing X509Data tag.";
+ return true;
+ }
+
+ // Parse <X509Certificate>
+ std::vector<xmlNodePtr> cert_vec =
+ GetChildren(X509_data_node, ns, kTokenX509Certificate);
+ if (cert_vec.empty()) {
+ LOG(ERROR) << "Missing X509Certificate tag.";
+ return false;
+ }
+
+ std::list<std::string> certificate_list;
+ for (auto certificate_node : cert_vec) {
+ certificate_list.push_back(
+ XmlStringToStdString(xmlNodeGetContent(certificate_node)));
+ }
+ data->set_certificate_list(certificate_list);
+ return true;
+}
+
+bool ParseObjectElement(
+ const xmlNodePtr node, const xmlNsPtr ns, SignatureData* data) {
+ xmlNodePtr object_node = GetFirstChild(node, ns, kTokenObject);
+ if (!object_node) {
+ LOG(ERROR) << "Missing Object tag.";
+ return false;
+ }
+
+ std::string object_id = GetAttribute(object_node, kTokenID);
+ data->set_object_id(object_id);
+ // Parse <SignatureProperties>
+ xmlNodePtr properties_node =
+ GetFirstChild(object_node, ns, kTokenSignatureProperties);
+ if (!properties_node) {
+ LOG(ERROR) << "Missing Object tag.";
+ return false;
+ }
+
+ std::vector<xmlNodePtr> prop_vec =
+ GetChildren(properties_node, ns, kTokenSignatureProperty);
+ std::string Id, element_name, profile_uri, role_uri;
+ for (xmlNodePtr sign_property_node : prop_vec) {
+ Id = GetAttribute(sign_property_node, kTokenID);
+ xmlNodePtr child = sign_property_node->children;
+ if (!child) {
+ LOG(ERROR) << "Failing to find " << element_name
+ << " element.";
+ return false;
+ }
+
+ if (Id.compare(kTokenAttrProfile) == 0) {
+ profile_uri = GetAttribute(child, kTokenURI);
+ data->set_profile_uri(profile_uri);
+ }
+ if (Id.compare(kTokenAttrRole) == 0) {
+ role_uri = GetAttribute(child, kTokenURI);
+ data->set_role_uri(role_uri);
+ }
+ }
+
+ return true;
+}
+
+bool ParseXML(xmlDocPtr docPtr, SignatureData* data) {
+ xmlNodePtr root = xmlDocGetRootElement(docPtr);
+ if (!root) {
+ LOG(ERROR) << "Missinging root node.";
+ return false;
+ }
+
+ // Look for the required namespace declaration.
+ xmlNsPtr signature_ns = GetNamespace(root, kExpectedXmlns);
+ if (!signature_ns) {
+ LOG(ERROR) << "Missinging or incorrect xmlns on signature tag.";
+ return false;
+ }
+ if (!TagNameEquals(root, kTokenSignature, signature_ns)) {
+ LOG(ERROR) << "Missinging Signature tag.";
+ return false;
+ }
+
+ if (!ParseSignedInfoElement(root, signature_ns, data))
+ return false;
+
+ if (!ParseSignatureValueElement(root, signature_ns, data))
+ return false;
+
+ if (!ParseKeyInfoElement(root, signature_ns, data))
+ return false;
+
+ if (!ParseObjectElement(root, signature_ns, data))
+ return false;
+
+ return true;
+}
+
+std::unique_ptr<SignatureData> SignatureParser::CreateSignatureData(
+ const boost::filesystem::path& signature_path, int signature_number) {
+ std::unique_ptr<SignatureData>
+ data(new SignatureData(signature_path, signature_number));
+
+ xmlInitParser();
+ xmlDocPtr doc = xmlParseFile(signature_path.string().c_str());
+ if (!doc) {
+ LOG(ERROR) << "Opening signature " << signature_path << " failed.";
+ return nullptr;
+ }
+
+ if (!ParseXML(doc, data.get())) {
+ LOG(ERROR) << "Parsering failed.";
+ xmlFreeDoc(doc);
+ return nullptr;
+ }
+
+ xmlFreeDoc(doc);
+ xmlCleanupParser();
+ return data;
+}
+
+} // namespace signature
--- /dev/null
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by an apache 2.0 license that can be
+// found in the LICENSE file.
+// Part of this file is redistributed from crosswalk project under BDS-style
+// license. Check LICENSE-xwalk file.
+
+#include <signature_validator.h>
+
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xmlschemas.h>
+
+#include <memory>
+#include <regex>
+#include <set>
+#include <string>
+
+#include <logging.h>
+#include <signature_data.h>
+#include <signature_parser.h>
+#include <signature_xmlsec_adaptor.h>
+
+namespace bf = boost::filesystem;
+
+namespace {
+
+const int kXMLLogSize = 1024;
+
+const char kAuthorSignatureName[] = "author-signature.xml";
+const char kTokenRoleAuthorURI[] =
+ "http://www.w3.org/ns/widgets-digsig#role-author";
+const char kTokenRoleDistributor[] =
+ "http://www.w3.org/ns/widgets-digsig#role-distributor";
+const char kTokenProfileURI[] =
+ "http://www.w3.org/ns/widgets-digsig#profile";
+const char kSignatureSchemaPath[] =
+ "/usr/share/app-installers/signature_schema.xsd";
+
+const std::regex kDistributorSignatureRegex("^signature([1-9][0-9]*)\\.xml$");
+
+// A wrapper of LOG(ERROR) function, which is used as parameter of function
+// xmlSchemaSetValidErrors
+void LogErrorLibxml2(void *, const char *msg, ...) {
+ char buffer[kXMLLogSize];
+ va_list args;
+ va_start(args, msg);
+ vsnprintf(buffer, sizeof(buffer), msg, args);
+ va_end(args);
+ LOG(ERROR) << "ERROR: " << buffer;
+}
+
+// A wrapper of LOG(WARNING) function, which is used as parameter of function
+// xmlSchemaSetValidErrors
+void LogWarningLibxml2(void *, const char *msg, ...) {
+ char buffer[kXMLLogSize];
+ va_list args;
+ va_start(args, msg);
+ vsnprintf(buffer, sizeof(buffer), msg, args);
+ va_end(args);
+ LOG(WARNING) << "Warning: " << buffer;
+}
+
+class SignatureFile {
+ public:
+ SignatureFile(const std::string& file_name, int file_number)
+ : file_name_(file_name), file_number_(file_number) {
+ }
+
+ std::string file_name() const {
+ return file_name_;
+ }
+
+ int file_number() const {
+ return file_number_;
+ }
+
+ bool operator<(const SignatureFile &second) const {
+ return file_number_ < second.file_number();
+ }
+
+ private:
+ std::string file_name_;
+ int file_number_;
+};
+typedef std::set<SignatureFile> SignatureFileSet;
+
+const SignatureFileSet GetSignatureFiles(
+ const boost::filesystem::path& widget_path) {
+ SignatureFileSet signature_set;
+
+ for (bf::directory_iterator dir_iterator(widget_path);
+ dir_iterator != bf::directory_iterator(); ++dir_iterator) {
+ const bf::path& path = dir_iterator->path();
+ if (path.extension() == ".xml" && bf::is_regular_file(path)) {
+ std::string file_name = path.filename().string();
+ if (file_name == kAuthorSignatureName) {
+ // Find author signature file.
+ signature_set.insert(SignatureFile(file_name, -1));
+ continue;
+ }
+ std::smatch match;
+ if (std::regex_match(file_name, match, kDistributorSignatureRegex)) {
+ if (match.size() == 2) {
+ // Find distributor signature file.
+ signature_set.insert(
+ SignatureFile(file_name, std::stoi(match[1].str())));
+ }
+ }
+ }
+ }
+ return signature_set;
+}
+
+bool XMLSchemaValidate(
+ const SignatureFile& signature_file,
+ const boost::filesystem::path& widget_path) {
+ xmlDocPtr schema_doc = xmlReadFile(
+ kSignatureSchemaPath, nullptr, XML_PARSE_NONET|XML_PARSE_NOENT);
+ if (!schema_doc) {
+ LOG(ERROR) << "Reading schema file failed.";
+ return false;
+ }
+
+ xmlSchemaParserCtxtPtr ctx = xmlSchemaNewParserCtxt(kSignatureSchemaPath);
+ if (!ctx) {
+ LOG(ERROR) << "Initing xml schema parser context failed.";
+ return false;
+ }
+
+ xmlSchemaPtr xschema = xmlSchemaParse(ctx);
+ if (!xschema) {
+ LOG(ERROR) << "Parsing xml schema failed.";
+ return false;
+ }
+
+ xmlSchemaValidCtxtPtr vctx = xmlSchemaNewValidCtxt(xschema);
+ if (!vctx) {
+ LOG(ERROR) << "Initing xml schema context failed.";
+ return false;
+ }
+ xmlSchemaSetValidErrors(vctx, (xmlSchemaValidityErrorFunc)&LogErrorLibxml2,
+ (xmlSchemaValidityWarningFunc)&LogWarningLibxml2, nullptr);
+
+ bf::path signature_path = widget_path / signature_file.file_name();
+ int ret = xmlSchemaValidateFile(vctx, signature_path.string().c_str(), 0);
+
+ if (ret != 0) {
+ LOG(ERROR) << "Validating " << signature_file.file_name()
+ << " schema failed.";
+ return false;
+ }
+ return true;
+}
+
+bool CheckObjectID(
+ const signature::SignatureData& signature_data) {
+ std::string object_id = signature_data.object_id();
+ std::set<std::string> reference_set = signature_data.reference_set();
+
+ std::set<std::string>::const_iterator result =
+ reference_set.find(std::string("#") + object_id);
+ if (result == reference_set.end()) {
+ LOG(ERROR) << "No reference to object.";
+ return false;
+ }
+ return true;
+}
+
+bool CheckRoleURI(
+ const signature::SignatureData& signature_data) {
+ std::string role_uri = signature_data.role_uri();
+
+ if (role_uri.empty()) {
+ LOG(ERROR) << "URI attribute in Role tag couldn't be empty.";
+ return false;
+ }
+
+ if (role_uri != kTokenRoleAuthorURI && signature_data.isAuthorSignature()) {
+ LOG(ERROR) << "URI attribute in Role tag does not "
+ << "match with signature filename.";
+ return false;
+ }
+
+ if (role_uri != kTokenRoleDistributor &&
+ !signature_data.isAuthorSignature()) {
+ LOG(ERROR) << "URI attribute in Role tag does not "
+ << "match with signature filename.";
+ return false;
+ }
+
+ return true;
+}
+
+bool CheckProfileURI(const signature::SignatureData& signature_data) {
+ if (kTokenProfileURI != signature_data.profile_uri()) {
+ LOG(ERROR) << "Profile tag contains unsupported value in URI attribute.";
+ return false;
+ }
+ return true;
+}
+
+boost::filesystem::path RelativePath(const boost::filesystem::path& path,
+ const boost::filesystem::path& root) {
+ if (root.empty())
+ return path;
+
+ std::string path_str = path.string();
+ std::string root_str = root.string();
+
+ if (root_str != path_str.substr(0, root_str.size()))
+ return bf::path();
+
+ auto index = root_str.size();
+
+ if (root_str[root_str.size() - 1] != '/') {
+ if (path_str[root_str.size()] != '/') {
+ return bf::path();
+ } else {
+ ++index;
+ }
+ }
+
+ return path_str.substr(index);
+}
+
+bool CheckReference(const signature::SignatureData& signature_data) {
+ const bf::path& widget_path = signature_data.GetExtractedWidgetPath();
+ const std::set<std::string>& reference_set = signature_data.reference_set();
+
+ for (bf::recursive_directory_iterator dir_iterator(widget_path);
+ dir_iterator != bf::recursive_directory_iterator(); ++dir_iterator) {
+ const bf::path& path = dir_iterator->path();
+ if (bf::is_regular_file(path)) {
+ std::string relative_path = RelativePath(path, widget_path).string();
+ if (relative_path == kAuthorSignatureName ||
+ std::regex_match(relative_path, kDistributorSignatureRegex)) {
+ // Skip signtature file.
+ continue;
+ }
+ std::set<std::string>::iterator ref_iter =
+ reference_set.find(relative_path);
+ if (ref_iter == reference_set.end()) {
+ LOG(ERROR) << relative_path << " is not in signature ds:Reference.";
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+} // anonymous namespace
+
+namespace signature {
+// static
+SignatureValidator::Status SignatureValidator::Check(
+ const boost::filesystem::path& widget_path) {
+ LOG(INFO) << "Verifying widget signature file.";
+ // Process every signature files (author and distributor) according to
+ // http://www.w3.org/TR/widgets-digsig/#signature-verification.
+ const SignatureFileSet& signature_set = GetSignatureFiles(widget_path);
+ if (signature_set.empty()) {
+ LOG(INFO) << "No signed signature in the package.";
+ return UNTRUSTED;
+ }
+
+ SignatureFileSet::reverse_iterator iter = signature_set.rbegin();
+ for (; iter != signature_set.rend(); ++iter) {
+ // Verify whether signature xml is a valid [XMLDSIG] document.
+ if (!XMLSchemaValidate(*iter, widget_path)) {
+ LOG(ERROR) << "Validating " << iter->file_name() << "schema failed.";
+ return INVALID;
+ }
+
+ std::unique_ptr<SignatureData> data = SignatureParser::CreateSignatureData(
+ widget_path / iter->file_name(), iter->file_number());
+ // Check whether each file in the widget can be found from ds:Reference.
+ if (!CheckReference(*data.get()))
+ return INVALID;
+
+ // Validate the profile property.
+ if (!CheckProfileURI(*data.get()))
+ return INVALID;
+
+ // Validate the identifier property.
+ if (!CheckObjectID(*data.get()))
+ return INVALID;
+
+ // Validate role property.
+ if (!CheckRoleURI(*data.get()))
+ return INVALID;
+
+ // Perform reference validation and signature validation on signature
+ if (!SignatureXmlSecAdaptor::ValidateFile(*data.get()))
+ return INVALID;
+ }
+ return VALID;
+}
+
+} // namespace signature
--- /dev/null
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Copyright (C) 2002-2003 Aleksey Sanin. All Rights Reserved.
+// Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by an apache 2.0 license that can be
+// found in the LICENSE file.
+// Part of this file is redistributed from crosswalk project under BDS-style
+// license. Check LICENSE-xwalk file.
+
+#include <signature_xmlsec_adaptor.h>
+
+#include <libxml/parser.h>
+#include <openssl/bio.h>
+#include <openssl/x509.h>
+#include <xmlsec/crypto.h>
+#include <xmlsec/io.h>
+#include <xmlsec/keysmngr.h>
+#include <xmlsec/xmlsec.h>
+#include <xmlsec/xmltree.h>
+#include <xmlsec/xmldsig.h>
+#ifndef XMLSEC_NO_XSLT
+#include <libxslt/xslt.h>
+#endif
+
+#include <boost/archive/iterators/binary_from_base64.hpp>
+#include <boost/archive/iterators/transform_width.hpp>
+#include <boost/filesystem/operations.hpp>
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <list>
+#include <map>
+#include <sstream>
+#include <string>
+#include <utility>
+
+#include <logging.h>
+
+namespace bai = boost::archive::iterators;
+namespace bf = boost::filesystem;
+
+namespace {
+
+const bf::path cert_prefix_path = "/usr/share/ca-certificates/tizen/";
+
+// TODO(XU): Once tizen platform provide certificate manager util APIs,
+// we should call API from system to query certificate's file path.
+class CertificateUtil {
+ public:
+ static const std::map<std::string, std::string>& certificate_path() {
+ return certificate_path_;
+ }
+
+ private:
+ static std::map<std::string, std::string> InitCertificatePath() {
+ std::map<std::string, std::string> root_certificates;
+ root_certificates["Tizen Partner-Manufacturer Distributor Root CA"] =
+ "tizen-distributor-root-ca-partner-manufacturer.pem";
+ root_certificates["SLP WebApp Temporary CA"] =
+ "tizen.root.preproduction.cert.pem";
+ root_certificates["Tizen Test Developer Root CA"] =
+ "tizen-developer-root-ca.pem";
+ root_certificates["Tizen Developers Root"] =
+ "tizen-developers-root.pem";
+ root_certificates["Tizen Partner Distributor Root CA"] =
+ "tizen-distributor-root-ca-partner.pem";
+ root_certificates["Tizen Partner-Operator Distributor Root CA"] =
+ "tizen-distributor-root-ca-partner-operator.pem";
+ root_certificates["Tizen Public Distributor Root CA"] =
+ "tizen-distributor-root-ca-public.pem";
+ root_certificates["Partner Class Developer Root"] =
+ "tizen-partner-class-developer-root.pem";
+ root_certificates["Partner Class Root Authority"] =
+ "tizen-partner-class-root-authority.pem";
+ root_certificates["Platform Class Developer Root"] =
+ "tizen-platform-class-developer-root.pem";
+ root_certificates["Platform Class Root Authority"] =
+ "tizen-platform-class-root-authority.pem";
+ root_certificates["Public Class Developer Root"] =
+ "tizen-public-class-developer-root.pem";
+ root_certificates["Public Class Root Authority"] =
+ "tizen-public-class-root-authority.pem";
+
+ return root_certificates;
+ }
+
+ static std::map<std::string, std::string> certificate_path_;
+};
+
+std::map<std::string, std::string>
+ CertificateUtil::certificate_path_ = InitCertificatePath();
+
+std::string ConvertBase64ToPemCert(const std::string& cert_in) {
+ return std::string("-----BEGIN CERTIFICATE-----") + cert_in +
+ "-----END CERTIFICATE-----";
+}
+
+std::string ConvertBase64ToDerCert(const std::string& cert_in) {
+ typedef bai::transform_width<
+ bai::binary_from_base64<std::string::const_iterator>, 8, 6>
+ base64_dec;
+
+ std::string cert = cert_in;
+
+ // remove newlines from xml format
+ cert.erase(std::remove(cert.begin(), cert.end(), '\n'), cert.end());
+
+ std::stringstream der;
+
+ // padding
+ unsigned int size = cert.size();
+ int padding = 0;
+ if (size && cert[size - 1] == '=') {
+ ++padding;
+ if (size && cert[size - 2] == '=') ++padding;
+ }
+
+ // replace padding to 'zero'
+ std::replace(cert.begin(), cert.end(), '=', 'A');
+
+ std::copy(base64_dec(cert.begin()), base64_dec(cert.end()),
+ std::ostream_iterator<char>(der));
+
+ // remove padding
+ cert.erase(cert.end() - padding, cert.end());
+
+ return der.str();
+}
+
+class XmlSecContext {
+ public:
+ static void GetExtractedPath(const signature::SignatureData& data);
+ static xmlSecKeysMngrPtr LoadTrustedCerts(
+ const signature::SignatureData& signature_data);
+ static bool VerifyFile(
+ xmlSecKeysMngrPtr mngr, const signature::SignatureData& data);
+
+ private:
+ static int FileMatchCallback(const char* file_name);
+ static void* FileOpenCallback(const char* file_name);
+ static int FileReadCallback(void* context, char* buffer, int len);
+ static int FileCloseCallback(void* context);
+ static boost::filesystem::path GetCertFromStore(const std::string& subject);
+
+ static bf::path prefix_path_;
+ static std::pair<void*, bool> file_wrapper_;
+};
+
+bf::path XmlSecContext::prefix_path_;
+std::pair<void*, bool> XmlSecContext::file_wrapper_;
+
+void XmlSecContext::GetExtractedPath(
+ const signature::SignatureData& data) {
+ prefix_path_ = data.GetExtractedWidgetPath();
+}
+
+int XmlSecContext::FileMatchCallback(const char* file_name) {
+ bf::path path = prefix_path_ / file_name;
+ return xmlFileMatch(path.string().c_str());
+}
+
+void* XmlSecContext::FileOpenCallback(const char* file_name) {
+ bf::path path = prefix_path_ / file_name;
+ XmlSecContext::file_wrapper_ =
+ std::make_pair(xmlFileOpen(path.string().c_str()), false);
+ return &(XmlSecContext::file_wrapper_);
+}
+
+int XmlSecContext::FileReadCallback(void* context, char* buffer, int len) {
+ std::pair<void*, bool>* file_wrapper =
+ static_cast<std::pair<void*, bool>*>(context);
+ assert(file_wrapper);
+ if (file_wrapper->second)
+ return 0;
+
+ int output = xmlFileRead(file_wrapper->first, buffer, len);
+ if (output == 0) {
+ file_wrapper->second = true;
+ xmlFileClose(file_wrapper->first);
+ }
+ return output;
+}
+
+int XmlSecContext::FileCloseCallback(void* context) {
+ std::pair<void*, bool>* file_wrapper =
+ static_cast<std::pair<void*, bool>*>(context);
+ assert(file_wrapper);
+ int output = 0;
+ if (!file_wrapper->second)
+ output = xmlFileClose(file_wrapper->first);
+
+ return output;
+}
+
+xmlSecKeysMngrPtr XmlSecContext::LoadTrustedCerts(
+ const signature::SignatureData& signature_data) {
+ xmlSecKeysMngrPtr mngr = xmlSecKeysMngrCreate();
+ if (!mngr) {
+ LOG(ERROR) << "Error: failed to create keys manager.";
+ return nullptr;
+ }
+ if (xmlSecCryptoAppDefaultKeysMngrInit(mngr) < 0) {
+ LOG(ERROR) << "Error: failed to initialize keys manager.";
+ xmlSecKeysMngrDestroy(mngr);
+ return nullptr;
+ }
+
+ std::list<std::string> certificate_list = signature_data.certificate_list();
+ std::string issuer;
+ for (std::list<std::string>::iterator it = certificate_list.begin();
+ it != certificate_list.end(); ++it) {
+ std::string cert = ConvertBase64ToDerCert(*it);
+
+ X509* x509_cert = nullptr;
+ const unsigned char* data_ptr =
+ reinterpret_cast<const unsigned char*>(cert.data());
+ if (!d2i_X509(&x509_cert, &data_ptr, cert.size())) {
+ LOG(ERROR) << "Cannot parse X509 certificate";
+ return nullptr;
+ }
+
+ X509_NAME* x509_name = X509_get_issuer_name(x509_cert);
+ if (!x509_name) {
+ LOG(ERROR) << "Cannot extract issuer of X509 certificate";
+ X509_free(x509_cert);
+ return nullptr;
+ }
+
+ std::array<char, 200> buffer;
+ if (X509_NAME_get_text_by_NID(x509_name,
+ NID_commonName, buffer.data(), buffer.size()) == -1) {
+ LOG(ERROR) << "Cannot extract DisplayName of X509 certificate";
+ X509_free(x509_cert);
+ return nullptr;
+ }
+ X509_free(x509_cert);
+
+ issuer = buffer.data();
+ LOG(INFO) << "Issuer: " << issuer;
+
+ std::string pem = ConvertBase64ToPemCert(*it);
+ if (xmlSecCryptoAppKeysMngrCertLoadMemory(mngr,
+ reinterpret_cast<const unsigned char*>(pem.c_str()), pem.size(),
+ xmlSecKeyDataFormatCertPem, xmlSecKeyDataTypeTrusted) < 0) {
+ LOG(ERROR) << "Error: failed to load pem certificate.";
+ xmlSecKeysMngrDestroy(mngr);
+ return nullptr;
+ }
+ }
+
+ // TODO(tiwanek): extract to seperate file...
+ const bf::path& root_cert_path =
+ XmlSecContext::GetCertFromStore(issuer);
+ if (!bf::exists(root_cert_path.string().c_str())) {
+ LOG(ERROR) << "Failed to find root certificate.";
+ return nullptr;
+ }
+
+ if (xmlSecCryptoAppKeysMngrCertLoad(mngr,
+ root_cert_path.string().c_str(), xmlSecKeyDataFormatPem,
+ xmlSecKeyDataTypeTrusted) < 0) {
+ LOG(ERROR) << "Error: failed to load root certificate";
+ xmlSecKeysMngrDestroy(mngr);
+ return nullptr;
+ }
+
+ return mngr;
+}
+
+bool XmlSecContext::VerifyFile(xmlSecKeysMngrPtr mngr,
+ const signature::SignatureData& data) {
+ LOG(INFO) << "Verify " << data.signature_file_name();
+ xmlSecIOCleanupCallbacks();
+ XmlSecContext::GetExtractedPath(data);
+ xmlSecIORegisterCallbacks(
+ XmlSecContext::FileMatchCallback,
+ XmlSecContext::FileOpenCallback,
+ XmlSecContext::FileReadCallback,
+ XmlSecContext::FileCloseCallback);
+
+ xmlDocPtr doc = xmlParseFile(data.signature_file_name().string().c_str());
+ if (!doc) {
+ LOG(ERROR) << "Error: failed to parse "
+ << data.signature_file_name().string();
+ return false;
+ }
+
+ if (!xmlDocGetRootElement(doc)) {
+ LOG(ERROR) << "Error: unable to get root element.";
+ xmlFreeDoc(doc);
+ return false;
+ }
+
+ xmlNodePtr node = xmlSecFindNode(
+ xmlDocGetRootElement(doc), xmlSecNodeSignature, xmlSecDSigNs);
+ if (!node) {
+ LOG(ERROR) << "Error: unable to find SecNodeSignature node.";
+ xmlFreeDoc(doc);
+ return false;
+ }
+
+ xmlSecDSigCtxPtr dsig_ctx = xmlSecDSigCtxCreate(mngr);
+ if (!dsig_ctx) {
+ LOG(ERROR) << "Error: failed to create signature context.";
+ xmlFreeDoc(doc);
+ return false;
+ }
+
+ if (xmlSecDSigCtxVerify(dsig_ctx, node) < 0) {
+ LOG(ERROR) << "Error: signature verify.";
+ xmlFreeDoc(doc);
+ xmlSecDSigCtxDestroy(dsig_ctx);
+ return false;
+ }
+
+ if (dsig_ctx->status != xmlSecDSigStatusSucceeded)
+ LOG(ERROR) << "Signature " << data.signature_file_name() <<" is INVALID";
+
+ LOG(INFO) << "Signature "<< data.signature_file_name() << " is OK.";
+
+ xmlFreeDoc(doc);
+ xmlSecDSigCtxDestroy(dsig_ctx);
+ return true;
+}
+
+boost::filesystem::path XmlSecContext::GetCertFromStore(
+ const std::string& subject) {
+ std::map<std::string, std::string>::const_iterator iter =
+ CertificateUtil::certificate_path().find(subject);
+
+ if (iter == CertificateUtil::certificate_path().end()) {
+ LOG(ERROR) << "Failing to find root certificate.";
+ return "";
+ }
+ LOG(INFO) << "root cert path is " << cert_prefix_path / iter->second;
+ return cert_prefix_path / iter->second;
+}
+
+} // namespace
+
+namespace signature {
+
+// static
+bool SignatureXmlSecAdaptor::ValidateFile(
+ const SignatureData& signature_data) {
+ xmlInitParser();
+ xmlSubstituteEntitiesDefault(1);
+#ifndef XMLSEC_NO_XSLT
+ xsltSecurityPrefsPtr xslt_sec_prefs = xsltNewSecurityPrefs();
+ xsltSetSecurityPrefs(
+ xslt_sec_prefs, XSLT_SECPREF_READ_FILE, xsltSecurityForbid);
+ xsltSetSecurityPrefs(
+ xslt_sec_prefs, XSLT_SECPREF_WRITE_FILE, xsltSecurityForbid);
+ xsltSetSecurityPrefs(
+ xslt_sec_prefs, XSLT_SECPREF_CREATE_DIRECTORY, xsltSecurityForbid);
+ xsltSetSecurityPrefs(
+ xslt_sec_prefs, XSLT_SECPREF_READ_NETWORK, xsltSecurityForbid);
+ xsltSetSecurityPrefs(
+ xslt_sec_prefs, XSLT_SECPREF_WRITE_NETWORK, xsltSecurityForbid);
+ xsltSetDefaultSecurityPrefs(xslt_sec_prefs);
+#endif // XMLSEC_NO_XSLT
+
+ if (xmlSecInit() < 0) {
+ LOG(ERROR) << "Error: xmlsec initialization failed.";
+ return false;
+ }
+
+ if (xmlSecCheckVersion() != 1) {
+ LOG(ERROR) << "Error: loaded xmlsec library version is not compatible.";
+ return false;
+ }
+
+#ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
+ if (xmlSecCryptoDLLoadLibrary(BAD_CAST XMLSEC_CRYPTO) < 0) {
+ LOG(ERROR) << "Error: unable to load default xmlsec-crypto library.";
+ return false;
+ }
+#endif // XMLSEC_CRYPTO_DYNAMIC_LOADING
+
+ if (xmlSecCryptoAppInit(nullptr) < 0) {
+ LOG(ERROR) << "Error: crypto initialization failed.";
+ return false;
+ }
+
+ if (xmlSecCryptoInit() < 0) {
+ LOG(ERROR) << "Error: xmlsec-crypto initialization failed.";
+ return false;
+ }
+
+ xmlSecKeysMngrPtr mngr = XmlSecContext::LoadTrustedCerts(signature_data);
+ if (!mngr)
+ return false;
+
+ if (!XmlSecContext::VerifyFile(mngr, signature_data)) {
+ xmlSecKeysMngrDestroy(mngr);
+ return false;
+ }
+
+ xmlSecKeysMngrDestroy(mngr);
+ xmlSecCryptoShutdown();
+ xmlSecCryptoAppShutdown();
+ xmlSecShutdown();
+
+#ifndef XMLSEC_NO_XSLT
+ xsltFreeSecurityPrefs(xslt_sec_prefs);
+ xsltCleanupGlobals();
+#endif // XMLSEC_NO_XSLT
+ xmlCleanupParser();
+
+ return true;
+}
+
+} // namespace signature
#include <cstring>
#include <cerrno>
#include <cassert>
+#include <memory>
#include <app_installer.h>
#include <step_unzip.h>
+#include "step/signature_step.h"
int
main (int argc, char **argv)
switch (pkgmgr_installer_get_request_type (pi))
{
case PKGMGR_REQ_INSTALL:
+ {
Installer = new AppInstaller(PKGMGR_REQ_INSTALL,(char*)pkgmgr_installer_get_request_info(pi),NULL);
step_unpack = new step_unzip();
Installer->AddStep(step_unpack);
+ // FIXME: unique_ptr because steps are not freed in installer.
+ std::unique_ptr<common::SignatureStep> signature_step(
+ new common::SignatureStep);
+ Installer->AddStep(signature_step.get());
+
Installer->Run();
break;
-
+ }
case PKGMGR_REQ_UNINSTALL:
break;