Skip to content

Commit

Permalink
Option to open container offline mode
Browse files Browse the repository at this point in the history
IB-7287

Signed-off-by: Raul Metsma <[email protected]>
  • Loading branch information
metsma committed Jan 27, 2022
1 parent 42a8cfd commit 3a42624
Show file tree
Hide file tree
Showing 12 changed files with 108 additions and 81 deletions.
2 changes: 2 additions & 0 deletions libdigidocpp.dox
Original file line number Diff line number Diff line change
Expand Up @@ -1400,6 +1400,8 @@ Signature Validation Policy
Default POLv2

http://open-eid.github.io/SiVa/siva/appendix/validation_policy/"</td></tr>
<tr><td>\-\-offline </td><td>Optional</td><td>
open container offline (eg. Don't send to SiVa)</td></tr>
<tr><td>\-\-warnings=

(ignore, warning, error) </td><td>Optional</td><td>
Expand Down
13 changes: 9 additions & 4 deletions libdigidocpp.i
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ namespace std {
%include "crypto/Signer.h"
%include "crypto/PKCS12Signer.h"
%include "crypto/PKCS11Signer.h"
#ifdef SWIGCSHARP
#ifdef _WIN32
%include "crypto/WinSigner.h"
#endif
%include "libdigidocpp.i.h"
Expand Down Expand Up @@ -277,16 +277,21 @@ namespace std {
}
}
%extend digidoc::Container {
static Container* open(const std::string &path, OpenFlags flags)
{
return Container::openPtr(path, flags).release();
}

Signature* prepareWebSignature(const std::vector<unsigned char> &cert, const std::string &profile = {},
const std::vector<std::string> &roles = {},
const std::string &city = {}, const std::string &state = {},
const std::string &postalCode = {}, const std::string &country = {})
{
class : public digidoc::Signer
class final: public digidoc::Signer
{
public:
digidoc::X509Cert cert() const override { return _cert; }
std::vector<unsigned char> sign(const std::string &, const std::vector<unsigned char> &) const override
digidoc::X509Cert cert() const final { return _cert; }
std::vector<unsigned char> sign(const std::string &, const std::vector<unsigned char> &) const final
{
THROW("Not implemented");
}
Expand Down
16 changes: 8 additions & 8 deletions src/ASiC_E.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ void ASiC_E::save(const string &path)
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.get()), zproperty(file->fileName()));
s.addFile(file->fileName(), *(static_cast<const DataFilePrivate*>(file)->m_is), zproperty(file->fileName()));

unsigned int i = 0;
for(Signature *iter: signatures())
Expand Down Expand Up @@ -158,9 +158,9 @@ void ASiC_E::addAdESSignature(istream &sigdata)
}
}

unique_ptr<Container> ASiC_E::openInternal(const string &path)
unique_ptr<Container> ASiC_E::openInternal(const string &path, OpenFlags flags)
{
DEBUG("ASiC_E::openInternal(%s)", path.c_str());
DEBUG("ASiC_E::openInternal(%s, %d)", path.c_str(), flags);
return unique_ptr<Container>(new ASiC_E(path));
}

Expand All @@ -181,13 +181,13 @@ void ASiC_E::createManifest(ostream &os)
try
{
Manifest manifest;
manifest.file_entry().push_back({"/", mediaType()});
manifest.file_entry().push_back(make_unique<File_entry>("/", mediaType()));
for(const DataFile *file: dataFiles())
manifest.file_entry().push_back({file->fileName(), file->mediaType()});
manifest.file_entry().push_back(make_unique<File_entry>(file->fileName(), file->mediaType()));

xml_schema::NamespaceInfomap map;
map["manifest"].name = ASiC_E::MANIFEST_NAMESPACE;
manifest::manifest(os, manifest, map, {}, xml_schema::Flags::dont_initialize);
manifest::manifest(os, manifest, map, "UTF-8", xml_schema::Flags::dont_initialize);
if(os.fail())
THROW("Failed to create manifest XML");
}
Expand Down Expand Up @@ -233,8 +233,8 @@ void ASiC_E::parseManifestAndLoadFiles(const ZipSerialize &z)
z.extract("META-INF/manifest.xml", manifestdata);
xml_schema::Properties p;
p.schema_location(ASiC_E::MANIFEST_NAMESPACE,
File::fullPathUrl(Conf::instance()->xsdPath() + "/OpenDocument_manifest.xsd"));
unique_ptr<xercesc::DOMDocument> doc = SecureDOMParser(p.schema_location(), true).parseIStream(manifestdata);
File::fullPathUrl(Conf::instance()->xsdPath() + "/OpenDocument_manifest.xsd"));
unique_ptr<xercesc::DOMDocument> doc = SecureDOMParser(p.schema_location(), true).parseIStream(manifestdata);
unique_ptr<Manifest> manifest = manifest::manifest(*doc, {}, p);

set<string> manifestFiles;
Expand Down
2 changes: 1 addition & 1 deletion src/ASiC_E.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ namespace digidoc
Signature* sign(Signer* signer) final;

static std::unique_ptr<Container> createInternal(const std::string &path);
static std::unique_ptr<Container> openInternal(const std::string &path);
static std::unique_ptr<Container> openInternal(const std::string &path, OpenFlags flags);

private:
ASiC_E();
Expand Down
12 changes: 6 additions & 6 deletions src/ASiC_S.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ void ASiC_S::addDataFile(const string &path, const string &mediaType)
{
if(!dataFiles().empty())
THROW("Can not add document to ASiC-S container which already contains a document.");

ASiContainer::addDataFile(path, mediaType);
}

Expand All @@ -80,11 +80,11 @@ void ASiC_S::addAdESSignature(istream & /*signature*/)
THROW("Not implemented.");
}

unique_ptr<Container> ASiC_S::openInternal(const string &path)
unique_ptr<Container> ASiC_S::openInternal(const string &path, OpenFlags flags)
{
if (!isContainerSimpleFormat(path))
return nullptr;
DEBUG("ASiC_S::openInternal(%s)", path.c_str());
DEBUG("ASiC_S::openInternal(%s, %d)", path.c_str(), flags);
return unique_ptr<Container>(new ASiC_S(path));
}

Expand Down Expand Up @@ -149,10 +149,10 @@ bool ASiC_S::isTimestampedASiC_S(const vector<string> &list)
{
DEBUG("isTimestampedASiC_S()");
bool isASiCS = false;

auto dataFiles = 0;
auto hasTimestamp = false;

// container has only one file in root folder and has a timestamp
for(const string &file: list)
{
Expand Down Expand Up @@ -191,7 +191,7 @@ bool ASiC_S::isContainerSimpleFormat(const string &path)
{
ZipSerialize z(path, false);
vector<string> list = z.list();
if(find(list.begin(), list.end(), "mimetype") != list.end())
if(find(list.cbegin(), list.cend(), "mimetype") != list.cend())
{
stringstream iss;
z.extract("mimetype", iss);
Expand Down
2 changes: 1 addition & 1 deletion src/ASiC_S.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ namespace digidoc
Signature* sign(Signer* signer) override;

static std::unique_ptr<Container> createInternal(const std::string &path);
static std::unique_ptr<Container> openInternal(const std::string &path);
static std::unique_ptr<Container> openInternal(const std::string &path, OpenFlags flags);

private:
ASiC_S();
Expand Down
25 changes: 19 additions & 6 deletions src/Container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,15 @@ using namespace digidoc;
using namespace std;
using namespace xercesc;

using plugin = unique_ptr<Container> (*)(const std::string &);
using pluginCreate = unique_ptr<Container> (*)(const std::string &);
using pluginOpen = unique_ptr<Container> (*)(const std::string &, Container::OpenFlags);

namespace digidoc
{
static string m_appName = "libdigidocpp";
static string m_userAgent = "libdigidocpp";
static vector<plugin> m_createList = {};
static vector<plugin> m_openList = {};
static vector<pluginCreate> m_createList = {};
static vector<pluginOpen> m_openList = {};
}

/**
Expand Down Expand Up @@ -349,13 +350,25 @@ Container* Container::open(const string &path)
* @throws Exception
*/
unique_ptr<Container> Container::openPtr(const string &path)
{
return openPtr(path, OpenDefault);
}

/**
* Opens container from a file
*
* @param path
* @param flags
* @throws Exception
*/
unique_ptr<Container> Container::openPtr(const string &path, OpenFlags flags)
{
for(auto open: m_openList)
{
if(unique_ptr<Container> container = open(path))
if(unique_ptr<Container> container = open(path, flags))
return container;
}
return ASiC_E::openInternal(path);
return ASiC_E::openInternal(path, flags);
}

/**
Expand Down Expand Up @@ -418,7 +431,7 @@ unique_ptr<Container> Container::openPtr(const string &path)
*
* It must contain static members:
* * static Container* createInternal(const std::string &path);
* * static Container* openInternal(const std::string &path);
* * static Container* openInternal(const std::string &path, OpenFlags flags);
*
* @see Container::create, Container::open
*/
Expand Down
5 changes: 5 additions & 0 deletions src/Container.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ DIGIDOCPP_EXPORT std::string version();
class DIGIDOCPP_EXPORT Container
{
public:
enum OpenFlags {
OpenDefault = 0,
OpenOffline = 1,
};
virtual ~Container();

virtual void save(const std::string &path = "") = 0;
Expand All @@ -66,6 +70,7 @@ class DIGIDOCPP_EXPORT Container
static std::unique_ptr<Container> createPtr(const std::string &path);
DIGIDOCPP_DEPRECATED static Container* open(const std::string &path);
static std::unique_ptr<Container> openPtr(const std::string &path);
static std::unique_ptr<Container> openPtr(const std::string &path, OpenFlags flags);
template<class T>
static void addContainerImplementation();

Expand Down
39 changes: 21 additions & 18 deletions src/SiVaContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ void SignatureSiVa::validate(const string &policy) const
}


SiVaContainer::SiVaContainer(const string &path, const string &ext, bool useHashCode)
SiVaContainer::SiVaContainer(const string &path, const string &ext, bool useHashCode, bool offline)
: d(new Private)
{
DEBUG("SiVaContainer::SiVaContainer(%s, %s, %d)", path.c_str(), ext.c_str(), useHashCode);
Expand All @@ -159,6 +159,10 @@ SiVaContainer::SiVaContainer(const string &path, const string &ext, bool useHash
d->dataFiles.push_back(new DataFilePrivate(move(ifs), File::fileName(path), "application/pdf", File::fileName(path)));
}

if(offline) {
return;
}

XMLByte buf[48*100];
string b64;
is->clear();
Expand All @@ -185,6 +189,7 @@ SiVaContainer::SiVaContainer(const string &path, const string &ext, bool useHash
Connect::Result r = Connect(url, "POST", 0, {}, CONF(verifyServiceCerts)).exec({
{"Content-Type", "application/json;charset=UTF-8"}
}, (const unsigned char*)req.c_str(), req.size());
req.clear();

if(!r.isOK() && !r.isStatusCode("400"))
THROW("Failed to send request to SiVa");
Expand Down Expand Up @@ -254,7 +259,7 @@ SiVaContainer::SiVaContainer(const string &path, const string &ext, bool useHash
for(const json &error: signature.value<json>("errors", {}))
{
string message = error["content"];
if(message.find("Bad digest for DataFile") == 0 && useHashCode)
if(message.find("Bad digest for DataFile") != string::npos && useHashCode)
THROW(message.c_str());
s->_exceptions.emplace_back(EXCEPTION_PARAMS(message.c_str()));
}
Expand All @@ -265,7 +270,7 @@ SiVaContainer::SiVaContainer(const string &path, const string &ext, bool useHash
if(message == "X509IssuerName has none or invalid namespace: null" ||
message == "X509SerialNumber has none or invalid namespace: null")
ex.setCode(Exception::IssuerNameSpaceWarning);
else if(message.find("Bad digest for DataFile") == 0)
else if(message.find("Bad digest for DataFile") != string::npos)
ex.setCode(Exception::DataFileNameSpaceWarning);
else if(message == "Old and unsupported format: SK-XML version: 1.0")
continue;
Expand All @@ -277,10 +282,8 @@ SiVaContainer::SiVaContainer(const string &path, const string &ext, bool useHash

SiVaContainer::~SiVaContainer()
{
for(const Signature *s: d->signatures)
delete s;
for(const DataFile *f: d->dataFiles)
delete f;
for_each(d->signatures.cbegin(), d->signatures.cend(), default_delete<Signature>());
for_each(d->dataFiles.cbegin(), d->dataFiles.cend(), default_delete<DataFile>());
delete d;
}

Expand Down Expand Up @@ -314,30 +317,28 @@ vector<DataFile *> SiVaContainer::dataFiles() const
return d->dataFiles;
}

unique_ptr<Container> SiVaContainer::openInternal(const string &path)
unique_ptr<Container> SiVaContainer::openInternal(const string &path, OpenFlags flags)
{
static const set<string> supported = {"PDF", "DDOC"};
string ext = File::fileExtension(path);
transform(ext.begin(), ext.end(), ext.begin(), ::toupper);
if(!supported.count(ext))
return {};
try {
return unique_ptr<Container>(new SiVaContainer(path, ext, true));
return unique_ptr<Container>(new SiVaContainer(path, ext, true, flags & OpenOffline));
} catch(const Exception &e) {
if(e.msg().find("Bad digest for DataFile") == 0)
return unique_ptr<Container>(new SiVaContainer(path, ext, false));
if(e.msg().find("Bad digest for DataFile") != string::npos)
return unique_ptr<Container>(new SiVaContainer(path, ext, false, flags & OpenOffline));
throw;
}
}

stringstream* SiVaContainer::parseDDoc(istream &is, bool useHashCode)
{
auto transcode = [](const XMLCh *chr) {
return xsd::cxx::xml::transcode<char>(chr);
};
using xsd::cxx::xml::transcode;
using cpXMLCh = const XMLCh*;
try
{
using cpXMLCh = const XMLCh*;
unique_ptr<DOMDocument> dom(SecureDOMParser().parseIStream(is));
DOMNodeList *nodeList = dom->getElementsByTagName(cpXMLCh(u"DataFile"));
for(XMLSize_t i = 0; i < nodeList->getLength(); ++i)
Expand All @@ -354,7 +355,9 @@ stringstream* SiVaContainer::parseDDoc(istream &is, bool useHashCode)
if(const XMLCh *b64 = item->getTextContent())
{
d->dataFiles.push_back(new DataFilePrivate(unique_ptr<istream>(new stringstream(base64_decode(b64))),
transcode(item->getAttribute(cpXMLCh(u"Filename"))), transcode(item->getAttribute(cpXMLCh(u"MimeType"))), transcode(item->getAttribute(cpXMLCh(u"Id")))));
transcode<char>(item->getAttribute(cpXMLCh(u"Filename"))),
transcode<char>(item->getAttribute(cpXMLCh(u"MimeType"))),
transcode<char>(item->getAttribute(cpXMLCh(u"Id")))));
}

if(!useHashCode)
Expand Down Expand Up @@ -387,7 +390,7 @@ stringstream* SiVaContainer::parseDDoc(istream &is, bool useHashCode)
catch(const XMLException& e)
{
try {
string result = transcode(e.getMessage());
string result = transcode<char>(e.getMessage());
THROW("Failed to parse DDoc XML: %s", result.c_str());
} catch(const xsd::cxx::xml::invalid_utf16_string & /* ex */) {
THROW("Failed to parse DDoc XML.");
Expand All @@ -396,7 +399,7 @@ stringstream* SiVaContainer::parseDDoc(istream &is, bool useHashCode)
catch(const DOMException& e)
{
try {
string result = transcode(e.getMessage());
string result = transcode<char>(e.getMessage());
THROW("Failed to parse DDoc XML: %s", result.c_str());
} catch(const xsd::cxx::xml::invalid_utf16_string & /* ex */) {
THROW("Failed to parse DDoc XML.");
Expand Down
4 changes: 2 additions & 2 deletions src/SiVaContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ class SiVaContainer final: public Container
Signature* sign(Signer* signer) final;

static std::unique_ptr<Container> createInternal(const std::string &path);
static std::unique_ptr<Container> openInternal(const std::string &path);
static std::unique_ptr<Container> openInternal(const std::string &path, OpenFlags flags);

private:
SiVaContainer(const std::string &path, const std::string &ext, bool useHashCode);
SiVaContainer(const std::string &path, const std::string &ext, bool useHashCode = true, bool offline = true);
DISABLE_COPY(SiVaContainer);

std::stringstream* parseDDoc(std::istream &is, bool useHashCode);
Expand Down
3 changes: 2 additions & 1 deletion src/digidoc-tool.1.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ Command open:
--warnings=(ignore,warning,error) - warning handling
--policy=(POLv1,POLv2) - Signature Validation Policy (default POLv2)
http://open-eid.github.io/SiVa/siva/appendix/validation_policy/
--extractAll[=path] - extracts documents (to path when provided)
--extractAll[=path] - extracts documents (to path when provided)
--offline - open container offline (eg. Don't send to SiVa)

Command add:
Example: digidoc-tool add --file=file1.txt container-file.asice
Expand Down
Loading

0 comments on commit 3a42624

Please sign in to comment.