Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate ASiC-S manifest containers #628

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 16 additions & 65 deletions src/ASiC_E.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
#include "crypto/Digest.h"
#include "crypto/Signer.h"
#include "util/File.h"
#include "util/ZipSerialize.h"

#include <algorithm>
#include <set>
Expand All @@ -36,10 +35,6 @@ using namespace digidoc;
using namespace digidoc::util;
using namespace std;

const string_view ASiC_E::ASIC_TM_PROFILE = "time-mark";
const string_view ASiC_E::ASIC_TS_PROFILE = "time-stamp";
const string_view ASiC_E::ASIC_TSA_PROFILE = "time-stamp-archive";
const string_view ASiC_E::ASIC_TMA_PROFILE = "time-mark-archive";
constexpr string_view MANIFEST_NS {"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"};

class ASiC_E::Private
Expand All @@ -65,7 +60,7 @@ ASiC_E::ASiC_E(const string &path)
, d(make_unique<Private>())
{
auto zip = load(path, true, {MIMETYPE_ASIC_E, MIMETYPE_ADOC});
parseManifestAndLoadFiles(*zip);
parseManifestAndLoadFiles(zip);
}

ASiC_E::~ASiC_E()
Expand All @@ -86,40 +81,21 @@ vector<DataFile*> ASiC_E::metaFiles() const
* document does not exist.
* @throws Exception is thrown if ASiC_E class is not correctly initialized.
*/
void ASiC_E::save(const string &path)
void ASiC_E::save(const ZipSerialize &s)
{
if(dataFiles().empty())
THROW("Can not save, container is empty.");
if(mediaType() != MIMETYPE_ASIC_E)
THROW("'%s' format is not supported", mediaType().c_str());

if(!path.empty())
zpath(path);
ZipSerialize s(zpath(), true);

stringstream mimetype;
mimetype << mediaType();
s.addFile("mimetype", mimetype, zproperty("mimetype"), ZipSerialize::DontCompress);

stringstream manifest;
createManifest(manifest);
s.addFile("META-INF/manifest.xml", manifest, zproperty("META-INF/manifest.xml"));

for(const DataFile *file: dataFiles())
s.addFile(file->fileName(), *(static_cast<const DataFilePrivate*>(file)->m_is), zproperty(file->fileName()));
if(!createManifest().save(s.addFile("META-INF/manifest.xml", zproperty("META-INF/manifest.xml"))))
THROW("Failed to create manifest XML");

std::set<Signatures*> saved;
unsigned int i = 0;
for(Signature *iter: signatures())
{
string file = Log::format("META-INF/signatures%u.xml", i++);
auto *signature = static_cast<SignatureXAdES_B*>(iter);
auto *signature = dynamic_cast<SignatureXAdES_B*>(iter);
if(!saved.insert(signature->signatures.get()).second)
continue;
stringstream ofs;
if(!signature->signatures->save(ofs))
string file = Log::format("META-INF/signatures%u.xml", i++);
if(!signature->signatures->save(s.addFile(file, zproperty(file))))
THROW("Failed to create signature XML file.");
s.addFile(file, ofs, zproperty(file));
}
}

Expand Down Expand Up @@ -165,14 +141,10 @@ unique_ptr<Container> ASiC_E::openInternal(const string &path)
/**
* Creates BDoc container manifest file and returns its path.
*
* Note: If non-ascii characters are present in XML data, we depend on the LANG variable to be set properly
* (see iconv --list for the list of supported encoding values for libiconv).
*
*
* @return returns created manifest file path.
* @throws Exception exception is thrown if manifest file creation failed.
*/
void ASiC_E::createManifest(ostream &os)
XMLDocument ASiC_E::createManifest()
{
DEBUG("ASiC_E::createManifest()");
auto doc = XMLDocument::create("manifest", MANIFEST_NS, "manifest");
Expand All @@ -185,40 +157,28 @@ void ASiC_E::createManifest(ostream &os)
add("/", mediaType());
for(const DataFile *file: dataFiles())
add(file->fileName(), file->mediaType());
if(!doc.save(os))
THROW("Failed to create manifest XML");
return doc;
}

/**
* Parses manifest file and checks that files described in manifest exist, also
* checks that no extra file do exist that are not described in manifest.xml.
*
* Note: If non-ascii characters are present in XML data, we depend on the LANG variable to be set properly
* (see iconv --list for the list of supported encoding values for libiconv).
*
* @param path directory on disk of the BDOC container.
* @throws Exception exception is thrown if the manifest.xml file parsing failed.
*/
void ASiC_E::parseManifestAndLoadFiles(const ZipSerialize &z)
{
DEBUG("ASiC_E::readManifest()");

const vector<string> &list = z.list();
auto mcount = size_t(count(list.cbegin(), list.cend(), "META-INF/manifest.xml"));
if(mcount < 1)
THROW("Manifest file is missing");
if(mcount > 1)
THROW("Found multiple manifest files");

try
{
stringstream manifestdata;
z.extract("META-INF/manifest.xml", manifestdata);
stringstream manifestdata = z.stringStream("META-INF/manifest.xml");
auto doc = XMLDocument::openStream(manifestdata, {"manifest", MANIFEST_NS});
doc.validateSchema(File::path(Conf::instance()->xsdPath(), "OpenDocument_manifest_v1_2.xsd"));

set<string_view> manifestFiles;
bool mimeFound = false;
auto doc = XMLDocument::openStream(manifestdata, {"manifest", MANIFEST_NS});
doc.validateSchema(File::path(Conf::instance()->xsdPath(), "OpenDocument_manifest_v1_2.xsd"));
for(auto file = doc/"file-entry"; file; file++)
{
auto full_path = file[{"full-path", MANIFEST_NS}];
Expand All @@ -239,12 +199,6 @@ void ASiC_E::parseManifestAndLoadFiles(const ZipSerialize &z)
if(full_path.back() == '/') // Skip Directory entries
continue;

auto fcount = size_t(count(list.cbegin(), list.cend(), full_path));
if(fcount < 1)
THROW("File described in manifest '%s' does not exist in container.", full_path.data());
if(fcount > 1)
THROW("Found multiple references of file '%s' in zip container.", full_path.data());

manifestFiles.insert(full_path);
if(mediaType() == MIMETYPE_ADOC &&
(full_path.compare(0, 9, "META-INF/") == 0 ||
Expand All @@ -256,7 +210,7 @@ void ASiC_E::parseManifestAndLoadFiles(const ZipSerialize &z)
if(!mimeFound)
THROW("Manifest is missing mediatype file entry.");

for(const string &file: list)
for(const string &file: z.list())
{
/**
* http://www.etsi.org/deliver/etsi_ts/102900_102999/102918/01.03.01_60/ts_102918v010301p.pdf
Expand All @@ -266,12 +220,9 @@ void ASiC_E::parseManifestAndLoadFiles(const ZipSerialize &z)
if(file.compare(0, 9, "META-INF/") == 0 &&
file.find("signatures") != string::npos)
{
if(count(list.begin(), list.end(), file) > 1)
THROW("Multiple signature files with same name found '%s'", file.c_str());
try
{
stringstream data;
z.extract(file, data);
stringstream data = z.stringStream(file);
auto signatures = make_shared<Signatures>(data, this);
for(auto s = signatures->signature(); s; s++)
addSignature(make_unique<SignatureXAdES_LTA>(signatures, s, this));
Expand All @@ -285,7 +236,7 @@ void ASiC_E::parseManifestAndLoadFiles(const ZipSerialize &z)

if(file == "mimetype" || file.compare(0, 8,"META-INF") == 0)
continue;
if(manifestFiles.find(file) == manifestFiles.end())
if(manifestFiles.count(file) == 0)
THROW("File '%s' found in container is not described in manifest.", file.c_str());
}
}
Expand All @@ -312,7 +263,7 @@ Signature* ASiC_E::prepareSignature(Signer *signer)

Signature *ASiC_E::sign(Signer* signer)
{
auto *s = static_cast<SignatureXAdES_LTA*>(prepareSignature(signer));
auto *s = dynamic_cast<SignatureXAdES_LTA*>(prepareSignature(signer));
try
{
s->setSignatureValue(signer->sign(s->signatureMethod(), s->dataToSign()));
Expand Down
14 changes: 7 additions & 7 deletions src/ASiC_E.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

namespace digidoc
{
class ZipSerialize;
class XMLDocument;

/**
* Implements the BDOC specification of the signed digital document container.
Expand All @@ -36,13 +36,12 @@ namespace digidoc
class ASiC_E final : public ASiContainer
{
public:
static const std::string_view ASIC_TM_PROFILE;
static const std::string_view ASIC_TS_PROFILE;
static const std::string_view ASIC_TMA_PROFILE;
static const std::string_view ASIC_TSA_PROFILE;
static constexpr std::string_view ASIC_TM_PROFILE = "time-mark";
static constexpr std::string_view ASIC_TS_PROFILE = "time-stamp";
static constexpr std::string_view ASIC_TMA_PROFILE = "time-mark-archive";
static constexpr std::string_view ASIC_TSA_PROFILE = "time-stamp-archive";

~ASiC_E() final;
void save(const std::string &path = {}) final;
std::vector<DataFile*> metaFiles() const;

void addAdESSignature(std::istream &data) final;
Expand All @@ -56,8 +55,9 @@ namespace digidoc
ASiC_E();
ASiC_E(const std::string &path);
DISABLE_COPY(ASiC_E);
void createManifest(std::ostream &os);
XMLDocument createManifest();
void parseManifestAndLoadFiles(const ZipSerialize &z);
void save(const ZipSerialize &s) final;

class Private;
std::unique_ptr<Private> d;
Expand Down
Loading